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

时间:2012-07-13 11:07:43

标签: python superclass metaclass class-method

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

将打印:in testMetaA: <class '__main__.testB'>;并且testB.do1(a)按预期工作:

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

但是,如果我尝试在包含“super”的元类中调用classmethod作为以下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

现在我想知道在类方法中使用super的最正确的方法是什么?是否应始终使用super(cls,cls)而不是显式编写当前的类名?

谢谢!

@jsbueno

  

如果某些代码采用动态创建派生类的技巧,那么这很重要 - 如果将该名称分配给另一个对象而不是类本身,则不应使用类名作为Super的第一参数。相反,类方法的cls或实例方法的self.__class__可以传递给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 Question的建议,我把python版本放在这里:sys.version = 2.7.2+(默认,2011年10月4日,20:06:09)[GCC 4.6.1]

1 个答案:

答案 0 :(得分:3)

  

但是,如果我尝试在元类中调用classmethod   包含一个“super”,如下面的testMetaB,它会引发一个错误:   NameError:未定义全局名称'testC'。

名称TestC只会在MetaClass完成它的工作后绑定到新类 - 这是在从__init__返回之后(__init__之前,{{1}方法。

当我们使用“超级”调用时,将类名称作为第一个参数,类名称不会神奇地出现在那里:它是一个(模块)全局变量,类本身被赋值 - 在正常的环境中。

在这种情况下,名称尚未分配 - 但是,由于它是一种类方法,因此yuu引用了__new__变量中的类 - 这就是它工作的原因。

如果某些代码采用动态创建派生类的技巧,那么这很重要 - 如果将该名称分配给另一个对象而不是类本身,则不应使用类名作为Super的第一参数。相反,类方法的cls或实例方法的cls可以传递给Super。

这是一个片段,显示了类名称的全局名称绑定是超级的:

self.__class__