I have a somewhat bizarre metaclass question. I'm using a meta class to dynamically create a 'sibling' class that inherits from another superclass and assign it as an attribute on the original class. Below is a minimal setup:
class Meta(type):
def __new__(cls, name, parents, dct):
sdct = dct.copy()
dct['sibling'] = type(name+'Sibling', (Mom,), sdct)
return super().__new__(cls, name, (Dad,), dct)
class Mom:
def __init__(self):
self.x = 3
class Dad:
def __init__(self):
self.x = 4
class Child(metaclass=Meta):
def __init__(self):
super().__init__()
self.y = 1 # <<< added from feedback
print(Child().x) # 4
print(Child().sibling) # <class '__main__.Child'> | should be ChildSibling
print(Child().sibling().x) # should be 3 instead throws:
# TypeError: super(type, obj): obj must be an instance or subtype of type
print(Child().sibling().y) # should print 4
Something seems to be going wrong above with the creation of the 'sibling' class but I'm not quite sure what. I know for example that this would work:
class ChildAbstract:
def __init__(self):
super().__init__()
ChildSibling = type('ChildSibling', (ChildAbstract, Mom), {})
Child = type('Child', (ChildAbstract, Dad), {'sibling': ChildSibling})
print(Child().sibling().x) # 3
I can't see the difference between the two cases though.
The dictionary sdct passed to type includes __qualname__
, which according to this PEP is what repr and str now use.
Try adding
print(Child is Child.sibling) # False
print(Child.sibling.__name__) # "ChildSibling"
and you'll see that it is truly the sibling.
As to why sibling().x
throws, the very same sdct also already contains Child.__init__
, which ends up as __init__
of your dynamically created new type ChildSibling
. During a call to sibling()
the super()
call resolves the class to Child
and is given an instance of ChildSibling
:
Also note that, aside from the zero argument form, super() is not limited to use inside methods. The two argument form specifies the arguments exactly and makes the appropriate references. The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.
https://docs.python.org/3/library/functions.html#super
Accessing the current instance is done by passing the first argument to method as instance.
super() -> same as super(__class__, <first argument>)
The error is raised at line 7210 of Object/typeobject.c.
Try removing the wrong __init__
in your __new__
with:
del sdct['__init__']
and now
print(Child().sibling().x)
will print 3.
A solution to "generic" inheritance and meta programming friendlier __init__
is to use the 2 argument form of super()
:
def __init__(self):
super(self.__class__, self).__init__()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With