我定义了一个元类,它将一个名为“test”的方法添加到创建的类中:
class FooMeta(type):
def __new__(mcls, name, bases, attrs):
def test(self):
return super().test()
attrs["test"] = test
cls = type.__new__(mcls, name, bases, attrs)
return cls
然后我使用这个元类创建两个类
class A(metaclass=FooMeta):
pass
class B(A):
pass
当我跑步时
a = A()
a.test()
在
super().test()
: 处引发 TypeError
super(type, obj): obj must be an instance or subtype of type
这意味着
super()
无法正确推断父类。如果我将 super
调用更改为
def __new__(mcls, name, bases, attrs):
def test(self):
return super(cls, self).test()
attrs["test"] = test
cls = type.__new__(mcls, name, bases, attrs)
return cls
那么引发的错误就变成:
AttributeError: 'super' object has no attribute 'test'
预计
A
的父级不会实现 test
方法。
所以我的问题是在动态添加的方法中调用
super()
的正确方法是什么?在这种情况下我应该总是写super(cls, self)
吗?如果是这样,那就太丑了(对于python3)!
无参数
super()
在Python中非常特别,因为它会在代码编译期间触发一些行为:Python创建一个不可见的__class__
变量,它是对“物理”
class
语句体的引用,是
super()
调用是嵌入的(如果在类方法中直接使用
__class__
变量,也会发生这种情况)。在这种情况下,调用
super()
的“物理”类是元类
FooMeta
本身,而不是它正在创建的类。解决方法是使用
super
的版本,它接受 2 个位置参数:将在其中搜索直接超类的类和实例本身。在Python 2和其他场合,人们可能更喜欢参数化使用
super
,使用类名本身作为第一个参数是正常的:在运行时,该名称将作为当前模块中的全局变量使用。也就是说,如果
class A
将使用
def test(...):
方法静态编码在源文件中,则您将在其主体内使用
super(A, self).test(...)
。但是,尽管类名在定义元类的模块中不能作为变量使用,但您确实需要将对类的引用作为第一个参数传递给
super
。由于(测试)方法接收
self
作为对实例的引用,因此其类由
self.__class__
或
type(self)
给出。
TL;DR:只需将动态方法中的超级调用更改为:
class FooMeta(type):
def __new__(mcls, name, bases, attrs):
def test(self):
return super(type(self), self).test()
attrs["test"] = test
cls = type.__new__(mcls, name, bases, attrs)
return cls