最近,我遇到了调用派生类方法的元类问题。
例如,我得到一个简单的基类testA
,它有一个classmethod do1(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
”的元类中调用classmethod作为以下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
现在我想知道在类方法中使用super的最正确的方法是什么?是否应始终使用super(cls,cls)
而不是显式编写当前的类名?
谢谢!
@jsbueno
如果某些代码采用动态创建派生类的技巧,那么这很重要 - 如果将该名称分配给另一个对象而不是类本身,则不应使用类名作为Super的第一参数。相反,类方法的cls或实例方法的
self.__class__
可以传递给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+(默认,2011年10月4日,20:06:09)[GCC 4.6.1]
答案 0 :(得分:3)
但是,如果我尝试在元类中调用classmethod 包含一个“super”,如下面的testMetaB,它会引发一个错误: NameError:未定义全局名称'testC'。
名称TestC
只会在MetaClass完成它的工作后绑定到新类 - 这是在从__init__
返回之后(__init__
之前,{{1}方法。
当我们使用“超级”调用时,将类名称作为第一个参数,类名称不会神奇地出现在那里:它是一个(模块)全局变量,类本身被赋值 - 在正常的环境中。
在这种情况下,名称尚未分配 - 但是,由于它是一种类方法,因此yuu引用了__new__
变量中的类 - 这就是它工作的原因。
如果某些代码采用动态创建派生类的技巧,那么这很重要 - 如果将该名称分配给另一个对象而不是类本身,则不应使用类名作为Super的第一参数。相反,类方法的cls
或实例方法的cls
可以传递给Super。
这是一个片段,显示了类名称的全局名称绑定是超级的:
self.__class__