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

相关问题