Valentino
Valentino

Reputation: 7361

Python factory class inheritance

How can I check if a class is child of a parent created by a factory class?

Consider the following trivial example:

from itertools import count
def basefactory():
    class Base:
        _idc = count(0)
        def __init__(self):
            self._id = next(self._idc)
    return Base

Then I create children using the factory class:

class A(basefactory()):
    def __init__(self):
        super().__init__()

class B(basefactory()):
    def __init__(self):
        super().__init__()

How can I test if A or B are children of a class created by the factory class?
Without the factory class I would use: issubclass(A, Base)) but of course this doesn't work with the factory class, since Base is not defined in the global space.

The reason of this setup is that I want A and B to have a different _idc counters to generate consecutive _ids for each instance of the child classes.
If A and B inherits directly from Base the counter is shared. Let me illustrate with an example what I mean.

With the factory class setup described above, if I do:

ll = [A(), A(), B(), B()]   
for e in ll:
    print(e._id, end=", ")

it prints: 0, 1, 0, 1, . If instead I move Base in the global space and A and B inherit directly from Base, the previous code prints: 0, 1, 2, 3, .

I want the _id to be 0, 1, 0, 1, , this is the reason I'm using the factory class setup. In my real case I don't have just A and B (I have 10 classes created by the factory class and they could increase in future), so I don't want to repeat myself and initialize the counters in each child.

However I also need to assert if a class is child of Base, or in other terms, if it is created through the factory class. And this is the question. Is it possible to do by checking the class hierarchy with issubclass? Or I need to change the setup or use a workaround like people suggested in the comments?

About the proposed duplicate

I don't think is a duplicate of Understanding Python super() with __init__() methods. I know what super does, and is not useful in this specific case. I want each child class to have a different counter, I don't see how referring to the parent class is helpful.
Maybe it is helpful (if so, please post an answer), but since the proposed duplicate doesn't deal with factory classes, I think this question is still different.

Upvotes: 2

Views: 1792

Answers (2)

chepner
chepner

Reputation: 532538

I think a decorator would do what you want (add an instance counter to a class) more simply than a factory function that generates an otherwise unnecessary base class.

from itertools import count

def add_counter(cls):
    cls._idc = count(0)
    old_init = cls.__init__
    def __init__(self, *args, **kwargs):
        old_init(self, *args, **kwargs)
        self._id  = next(self._idc)
    cls.__init__ = __init__
    return cls

@add_counter
class A:
    pass

@add_counter
class B:
    pass

ll = [A(), A(), B(), B()]
for e in ll:
    print(e._id, end=", ")

Upvotes: 2

sashaboulouds
sashaboulouds

Reputation: 1864

Well, I don't know how to access this information properly, but when typing help(A), you get Base:

Help on class A in module __main__:
class A(Base)
 |  Method resolution order:
 |      A
 |      Base
 |      builtins.object
 |  
 |  Methods inherited from Base:
 |  
 |  __init__(self)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Base:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

I get it must be possible to access it properly

Upvotes: 0

Related Questions