最近,我遇到了一個調用派生類方法的元類的問題。 例如,我得到一個簡單的基類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