最近,我遇到了一个调用派生类方法的元类的问题。 例如,我得到一个简单的基类testA
,其中有一个类方法do1(a)
在python中派生类方法的正确方法是什么?
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(cls,cls)
而不是显式编写当前的类名?
谢谢!
@jsbueno
If some piece of code resorts to tricks like dynamically creating derived classes, that is important - one should not use the class name as first parametere to Super if that name is assigned to another object than the class itself. Instead, cls for class methods, or
self.__class__
for instance methods can be passed to 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问题的建议,我在这里把Python版本:内容sys.version = 2.7.2+(默认情况下,2011年10月4日,20时06分09秒)[GCC 4.6.1]
@Don问:“你的元类testMetaA其实没有建立一个一流的。”它会的,你可以尝试将函数包装在'__init__'中。如果不重写,元类“__new__”并不意味着它不会使用超类的'__new__'。你可以在网上找到很多例子。你应该注意到,和普通类一样,元类的第一个arg和元类的第一个arg完全不同。 – Wang 2012-07-13 11:25:50
除了要修改类名或基元组外,您始终可以使用'__init__'。我认为它比__new__更方便,因为在__new__之后的__init__中,元类的实例cls确实存在。因为在'__new__'中,可以使用'cls = super(testMetaB,meta).__ new __(meta,cname,bases,cdict)'来首先构建cls。然后在'__new__'中修改它。 – Wang 2012-07-13 11:36:30
对我来说'self'实际上是指一个普通类的实例。我通常使用'meta'作为元类的'__new__'的第一个参数,'cls'指向元类的实例(这是一个普通的类)。你可以使用任何你想要的单词,这并不重要。无论如何,这与我的问题完全无关。 – Wang 2012-07-13 11:40:28