python元类继承问题
Posted
技术标签:
【中文标题】python元类继承问题【英文标题】:python metaclass inheritance issue 【发布时间】:2016-07-19 02:12:53 【问题描述】:我有一个有点奇怪的元类问题。我正在使用元类动态创建一个继承自另一个超类的“兄弟”类,并将其分配为原始类的属性。下面是一个最小的设置:
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
上面的“兄弟”类的创建似乎出了点问题,但我不太确定是什么。例如,我知道这会起作用:
class ChildAbstract:
def __init__(self):
super().__init__()
ChildSibling = type('ChildSibling', (ChildAbstract, Mom), )
Child = type('Child', (ChildAbstract, Dad), 'sibling': ChildSibling)
print(Child().sibling().x) # 3
不过,我看不出这两种情况之间的区别。
【问题讨论】:
【参考方案1】:传递给type的字典sdct包括__qualname__
,根据这个PEP是repr和str 现在使用。
尝试添加
print(Child is Child.sibling) # False
print(Child.sibling.__name__) # "ChildSibling"
你会发现它确实是兄弟姐妹。
至于为什么sibling().x
抛出,同样的sdct 也已经包含Child.__init__
,它最终是您动态创建的新类型ChildSibling
的__init__
。在调用sibling()
期间,super()
调用将类解析为Child
并获得ChildSibling
的实例:
还要注意,除了零参数形式之外,super() 不限于使用内部方法。两个参数形式精确地指定参数并进行适当的引用。零参数形式仅适用于类定义,编译器填写必要的详细信息以正确检索正在定义的类,以及访问普通方法的当前实例。
https://docs.python.org/3/library/functions.html#super
通过将第一个参数作为实例传递给方法来访问当前实例。
super() -> same as super(__class__, <first argument>)
在line 7210 of Object/typeobject.c 引发错误。
尝试删除__new__
中的错误__init__
:
del sdct['__init__']
现在
print(Child().sibling().x)
将打印 3。
“通用”继承和元编程友好的解决方案 __init__
是使用 super()
的 2 参数形式:
def __init__(self):
super(self.__class__, self).__init__()
【讨论】:
有趣的一点,从字典中删除 init 是可行的,但这只是因为在我的玩具示例中,我在 Child.__init__ 中没有做任何事情,所以这并不能完全解决问题(更新代码来说明)。我会看看 qualname 不过 啊,关于 super 的注释说明了这一点 @Buck 您的第二个示例有效的原因是这两种动态类型的基础包括ChildAbstract
,因此super()
很高兴。
所以super().__init__() -> super(self.__class__, self).__init__()
解决了这个问题,谢谢@Ilja
@Buck 也将您的解决方案作为答案的一部分,因此更容易找到以上是关于python元类继承问题的主要内容,如果未能解决你的问题,请参考以下文章