2012-07-13 166 views
3

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

+0

@Don問:「你的元類testMetaA其實沒有建立一個一流的。」它會的,你可以嘗試將函數包裝在'__init__'中。如果不重寫,元類「__new__」並不意味着它不會使用超類的'__new__'。你可以在網上找到很多例子。你應該注意到,和普通類一樣,元類的第一個arg和元類的第一個arg完全不同。 – Wang 2012-07-13 11:25:50

+0

除了要修改類名或基元組外,您始終可以使用'__init__'。我認爲它比__new__更方便,因爲在__new__之後的__init__中,元類的實例cls確實存在。因爲在'__new__'中,可以使用'cls = super(testMetaB,meta).__ new __(meta,cname,bases,cdict)'來首先構建cls。然後在'__new__'中修改它。 – Wang 2012-07-13 11:36:30

+0

對我來說'self'實際上是指一個普通類的實例。我通常使用'meta'作爲元類的'__new__'的第一個參數,'cls'指向元類的實例(這是一個普通的類)。你可以使用任何你想要的單詞,這並不重要。無論如何,這與我的問題完全無關。 – Wang 2012-07-13 11:40:28

回答

3

However, if I try to call the classmethod inside the metaclass which contains a "super" as following testMetaB, it will raise an error: NameError: global name 'testC' is not defined.

名稱TestC將只綁定到新的後級的MetaClass完成了它的工作 - 這是回國後從它的__init__(和__init__前,__new__)方法。

當我們使用「super」調用將類名稱作爲第一個參數時,類名不會在這裏神奇地出現:它是一個(模塊)全局變量,類本身被分配給它 - 在正常情況下。

在這種情況下,名稱尚未分配 - 但是,因爲它是一個類方法,所以yuu對cls變量中類的引用 - 這就是它起作用的原因。

如果某段代碼使用動態創建派生類的技巧,那很重要 - 如果該名稱被指定給另一個對象而不是該類本身,則不應該使用類名作爲Super的第一個參數。相反,cls用於類方法,或者self.__class__用於實例方法可以傳遞給Super。

下面是該全球名稱類名綁定一個片段是什麼超級採取:

>>> class A(object): 
... def do(self): 
...  print "In class A" 
... 
>>> class B(A): 
... def do(self): 
...  super(B, self).do() 
... 
>>> C = B 
>>> C().do() 
In class A 
>>> class B(object): 
... def do(self): 
...  print "in new class B" 
... 
>>> C().do() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in do 
TypeError: super(type, obj): obj must be an instance or subtype of type 
>>> 
+0

如果你能指出更多點,在關鍵部分會發生什麼,那將會很好。如果我理解正確,你試圖演示如何通過C()調用第二個類B嗎? – 2012-07-13 11:53:00

+0

謝謝!對於正常的方法,我通常會這樣做:'super(type(self),self).do()',我認爲這可能比'self .__ class__'好? – Wang 2012-07-13 11:59:21

+0

王:事實上,類型(自我)會比「自我.__類別」更好 - 但兩者都是相同的。 – jsbueno 2012-07-13 12:14:09

相關問題