Reputation: 1168
I have the following class structure:
class Base:
def z(self):
raise NotImplementedError()
class A(Base):
def z(self):
self._x()
return self._z()
def _x(self):
# do stuff
def _a(self):
raise NotImplementedError()
class B(Base)
def z(self):
self._x()
return self._z()
def _x(self):
# do stuff
def _z(self):
raise NotImplementedError()
class C(A):
def _z(self):
print(5)
class D(B):
def _z(self):
print(5)
The implementation of C(A)
and D(B)
is exactly the same and does not really care which class it inherits from. The conceptual difference is only in A
and B
(and these need to be kept as separate classes). Instead of writing separate definitions for C
and D
, I want to be able to dynamically inherit from A
or B
based on an argument provided at time of creating an instance of C
/D
(eventually C
and D
must be the same name).
It seems that metaclasses might work, but I am not sure how to pass an __init__
argument to the metaclass __new__
(and whether this will actually work). I would really prefer a solution which resolves the problem inside the class.
Upvotes: 2
Views: 64
Reputation: 81594
Have you considered using composition instead of inheritance? It seems like it is much more suitable for this use case. See the bottom of the answer for details.
Anyway,
class C(A): ......... class C(B): .....
is not even valid, and will result with only class C(B)
getting defined.
I'm not sure a metaclass will be able to help you here. I believe the best way would be to use type
but I'd love to be corrected.
A solution using type
(and probably misusing locals()
but that's not the point here)
class A:
def __init__(self):
print('Inherited from A')
class B:
def __init__(self):
print('Inherited from B')
class_to_inherit = input() # 'A' or 'B"
C = type('C', (locals()[class_to_inherit],), {})
C()
'A' or 'B'
>> A
Inherited from A
'A' or 'B'
>> B
Inherited from B
Composition
Tracking back to the question in the beginning of my answer, you state yourself that the implementation of both "C(A)
" and "C(B)
" is identical and they don't actually care about A
or B
. It seems more correct to me to use composition. Then you can do something along the lines of:
class A: pass
class B: pass
class C:
def __init__(self, obj): # obj is either A or B instance, or A or B themselves
self.obj = obj # or self.obj = obj() if obj is A or B themselves
c = C(A()) # or c = C(A)
In case C
should expose the same API as A
or B
, C
can overwrite __getattr__
:
class A:
def foo(self):
print('foo')
class C:
def __init__(self, obj):
self.obj = obj
def __getattr__(self, item):
return getattr(self.obj, item)
C(A()).foo()
# foo
Upvotes: 1