最近遇到一个元类调用派生类方法的问题。例如,我得到一个简单的 baseclass testA
,它有一个 classmethoddo1(a)
class testA(object):
@classmethod
def do1(cls, a):
print "in testA:",cls, a
然后我构建了一个元类,它实际上只打印 cls:
class testMetaA(type):
def __init__(cls,cname,bases,cdict):
print "in testMetaA: %s"%cls
然后我可以使用元类来构建一个子类testB
,它可以按预期工作:
class testB(testA):
@classmethod
def do1(cls, a):
print "in testB: %s"%cls
super(testB, cls).do1(a)
__metaclass__=testMetaA
它将打印in testMetaA: <class '__main__.testB'>
:和testB.do1(a)
预期的作品:
>>> testB.do1('hello')
in testB: <class '__main__.testB'>
in testA: <class '__main__.testB'> hello
但是,如果我尝试在包含“ super
”的元类中调用类方法,如下所示testMetaB
,它将引发错误:NameError: global name 'testC' is not defined
。
class testMetaB(type):
def __init__(cls,cname,bases,cdict):
print "in testMetaB: %s"%cls
cls.do1("hello")
class testC(testA):
@classmethod
def do1(cls, a):
print "in testC: %s"%cls
super(testC, cls).do1(a)
__metaclass__=testMetaB
我终于找到了一种通过使用super(cls, cls)
而不是解决它的方法super(testC, cls)
:
class testD(testA):
@classmethod
def do1(cls, a):
print "in testD: %s"%cls
super(cls, cls).do1(a)
__metaclass__=testMetaB
它将打印为:
in testMetaB: <class '__main__.testD'>
in testD: <class '__main__.testD'>
in testA: <class '__main__.testD'> hello
testD.do1(a)
也可以按预期工作:
>>> testD.do1('Well done')
in testD: <class '__main__.testD'>
in testA: <class '__main__.testD'> Well done
现在我想知道在 classmethod 中使用 super 的最正确方法是什么?是否应该始终使用super(cls,cls)
而不是显式编写当前类名?
谢谢!
@jsbueno
如果某些代码采用动态创建派生类之类的技巧,这很重要 - 如果该名称被分配给另一个对象而不是类本身,则不应使用类名作为 Super 的第一个参数。相反,类方法或
self.__class__
实例方法的 cls 可以传递给 Super。
这是否意味着通常使用类名来超级是一个坏主意?
对我自己来说,我通常使用super(type(self),self)
而不是super(type(self.__class__),self)
普通方法。不知道有没有什么大的优势可以使用self.__class__
。我像这样重复@jsbueno 示例,这里是 C 使用super(type(self),self)
. 所以在类被改变D2()
时不会改变行为。C
>>> class A(object):
def do(self):
print "in class A"
>>> class B(A):
def do(self):
super(B, self).do()
>>> class C(A):
def do(self):
super(type(self),self).do()
>>> D1=B
>>> D2=C
>>> D1().do()
in class A
>>> D2().do()
in class A
>>> class B(A):
def do(self):
print "in new class B"
>>> D1().do()
Traceback (most recent call last):
File "<pyshell#52>", line 1, in <module>
D1().do()
File "<pyshell#37>", line 3, in do
super(B, self).do()
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> class C(A):
def do(self):
print "in new class C"
>>> D2().do()
in class A
根据@Don Question的建议,我将python版本放在这里: sys.version= 2.7.2+ (default, Oct 4 2011, 20:06:09) [GCC 4.6.1]