0

I wrote a Python module, with several classes that inherit from a single class called MasterBlock. I want to import this module in a script, create several instances of these classes, and then get a list of all the existing instances of all the childrens of this MasterBlock class. I found some solutions with vars()['Blocks.MasterBlock'].__subclasses__() but as the instances I have are child of child of MasterBlock, it doesn't work.

Here is some example code:

Module:

Class MasterBlock:
    def main(self):
        pass
Class RandomA(MasterBlock):
    def __init__(self):
        pass
    # inherit the main function
Class AnotherRandom(MasterBlock):
    def __init__(self):
        pass
    # inherit the main function

Script:

import module
a=module.RandomA()
b=module.AnotherRandom()
c=module.AnotherRandom()
# here I need to get list_of_instances=[a,b,c]

Th ultimate goal is to be able to do:

for instance in list_of_instances:
    instance.main()
CoMartel
  • 3,521
  • 4
  • 25
  • 48
  • You can combine a solution of [Listing all user methods of a class](http://stackoverflow.com/questions/1911281/how-do-i-get-list-of-methods-in-a-python-class) and [Calling a class method by string representation](http://stackoverflow.com/questions/3061/calling-a-function-of-a-module-from-a-string-with-the-functions-name-in-python). – Torxed May 18 '15 at 08:28
  • @Torxed How would this work? The question asks for a list of _instances_, not _attributes_. – Eric O. Lebigot May 18 '15 at 08:32
  • Using global state like this is generally a bad idea. It's probably better to just keep track of the `[a, b, c]` list as you create `a`, `b`, and `c`. – user2357112 May 18 '15 at 08:36
  • 1
    @user2357112 How do you want to keep track of this? I would say that the _automatic_ tracking that the question asks for is robust. – Eric O. Lebigot May 18 '15 at 08:43
  • @EOL: `list_of_instances = [a, b, c]`? – user2357112 May 18 '15 at 08:44
  • @user2357112 This is dangerous (error prone), in general: what if someone modifies the code and does not know that/whether `list_of_instances` should be updated? The idea behind the question is that this bookkeeping be automatic, so as to avoid errors. – Eric O. Lebigot May 18 '15 at 08:45

3 Answers3

2

What about adding a class variable, that contains all the instances of MasterBlock? You can record them with:

Class MasterBlock(object):

    all_instances = []  # All instances of MasterBlock

    def __init__(self,…):
        …
        self.all_instances.append(self)  # Not added if an exception is raised before

You get all the instances of MasterBlock with MasterBlock.all_instances (or instance.all_instances).

This works if all base classes call the __init__ of the master class (either implicitly through inheritance or explicitly through the usual super() call).

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
2

Here's a way of doing that using a class variable:

class MasterBlock(object):
    instances = []
    def __init__(self):
        self.instances.append(self)
    def main(self):
        print "I am", self

class RandomA(MasterBlock):
    def __init__(self):
        super(RandomA, self).__init__()
        # other init...

class AnotherRandom(MasterBlock):
    def __init__(self):
        super(AnotherRandom, self).__init__()
        # other init...


a = RandomA()
b = AnotherRandom()
c = AnotherRandom()
# here I need to get list_of_instances=[a,b,c]

for instance in MasterBlock.instances:
    instance.main()

(you can make it simpler if you don't need __init__ in the subclasses)

output:

I am <__main__.RandomA object at 0x7faa46683610>
I am <__main__.AnotherRandom object at 0x7faa46683650>
I am <__main__.AnotherRandom object at 0x7faa46683690>
Emile
  • 2,946
  • 2
  • 19
  • 22
  • This looks exactly like my answer… Why add this? In my eyes, this mostly adds noise and should be deleted. If you want to make it explicit how to use `super()`, you can do so either in a comment instead, or you can even edit my answer. – Eric O. Lebigot May 18 '15 at 08:37
  • because I wrote it before you posted your answer so, eh, might as well post it anyway :) – Emile May 18 '15 at 08:38
  • You should have reloaded the page: my answer came 6–3 minutes before yours. :) StackOverflow displays a banner, when a new answer appears. I do think that your answer is good, but it is essentially a duplicate, so I would delete it so as to not waste the time of those who read it (they won't gain much new information). – Eric O. Lebigot May 18 '15 at 08:39
  • Well that is a very clear and complete answer, so I will take it! Thank you very much! – CoMartel May 18 '15 at 09:14
2

If you add a __new__() method as shown below to your base class which keeps track of all instances created in a class variable, you could make the process more-or-less automatic and not have to remember to call something in the __init__() of each subclass.

class MasterBlock(object):
    instances = []
    def __new__(cls, *args, **kwargs):
        instance = super(MasterBlock, cls).__new__(cls, *args, **kwargs)
        instance.instances.append(instance)
        return instance

    def main(self):
        print('in main of', self.__class__.__name__)  # for testing purposes

class RandomA(MasterBlock):
    def __init__(self):
        pass
    # inherit the main function

class AnotherRandom(RandomA):  # works for sub-subclasses, too
    def __init__(self):
        pass
    # inherit the main function

a=RandomA()
b=AnotherRandom()
c=AnotherRandom()

for instance in MasterBlock.instances:
    instance.main()

Output:

in main of RandomA
in main of AnotherRandom
in main of AnotherRandom
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Well, I have too much good answers to this question! Thanks a lot, I did'nt know about `__new__()` and it is exactly what I needed here! – CoMartel May 18 '15 at 09:56
  • 1
    You're welcome. Writing a class `__new__()` method isn't necessary very often, but knowing that it's available and when it's called during the creation of instances (if defined) can be helpful in certain situations, as illustrated. This will also work if you ever define sub-subclasses, btw. – martineau May 18 '15 at 10:04