在python中派生类方法的正确方法是什么?

最近,我遇到了元类调用派生类方法的问题.
例如,我得到一个简单的基类testA,它具有一个类方法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

它将打印:在testMetaA中:< class'__main __.testB'&gt ;;并且testB.do1(a)可以按预期工作:

>>> testB.do1('hello')
in testB: <class '__main__.testB'>
in testA: <class '__main__.testB'> hello

但是,如果我尝试按以下testMetaB调用包含“超级”的元类内部的类方法,则会引发错误:NameError:未定义全局名称“ testC”.

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

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是个坏主意?

对于我自己,我通常使用super(type(self),self)而不是super(type(self .__ class __),self)作为常规方法.我不知道使用self .__ class__是否有任何主要优势.
我重复这样的@jsbueno示例,这里的C使用super(type(self),self).因此,当类C更改时,D2()不会更改行为.

>>> 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]

解决方法:

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.

下面的代码片段显示了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
>>> 
上一篇:自定义KVO(三)


下一篇:java-超类和子类错误