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

在python中派生类方法的正确方法是什么?,python,superclass,metaclass,class-method,Python,Superclass,Metaclass,Class Method,最近,我遇到了一个元类调用派生类方法的问题。 例如,我得到一个简单的基类testA,它有一个classmethoddo1(a) 然后我构建了一个元类,它实际上什么都不做,只打印cls: class testMetaA(type): def __init__(cls,cname,bases,cdict): print "in testMetaA: %s"%cls 然后,我可以使用元类构建一个子类testB,它可以按预期工作: class testB(testA):

最近,我遇到了一个元类调用派生类方法的问题。 例如,我得到一个简单的基类testA,它有一个classmethod
do1(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
>>> testB.do1('hello')
in testB: <class '__main__.testB'>
in testA: <class '__main__.testB'> hello
>>> testD.do1('Well done')
in testD: <class '__main__.testD'>
in testA: <class '__main__.testD'> Well done
它将在testMetaA:中打印:
;而
testB.do1(a)
的工作原理与预期一致:

class testB(testA):

    @classmethod
    def do1(cls, a):
        print "in testB: %s"%cls
        super(testB, cls).do1(a)
    __metaclass__=testMetaA
>>> testB.do1('hello')
in testB: <class '__main__.testB'>
in testA: <class '__main__.testB'> hello
>>> testD.do1('Well done')
in testD: <class '__main__.testD'>
in testA: <class '__main__.testD'> Well done
我终于找到了一种解决方法,用
super(cls,cls)
代替
super(testC,cls)

它将打印为:

in testMetaB: <class '__main__.testD'>
in testD: <class '__main__.testD'>
in testA: <class '__main__.testD'> hello
现在我想知道在类方法中使用super最正确的方法是什么?是否应该始终使用
super(cls,cls)
而不是显式编写当前类名

谢谢

@杰斯布埃诺

如果某段代码采用了动态创建派生类之类的技巧,这一点很重要——如果将类名指定给类本身以外的另一个对象,则不应将该类名用作Super的第一个参数。相反,可以将类方法的cls,或实例方法的
self.\uuuuu类传递给Super

这是否意味着在一般情况下使用类名进行super是个坏主意?

对我自己来说,我通常使用
super(type(self),self)
而不是
super(type(self.\uuuu class\uuuuu),self)
作为常规方法。我不知道使用
self.\uuuu class\uuu
是否有任何主要优势。 我重复@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
>>A类(对象):
def do(自我):
打印“A类”
>>>B(A)类:
def do(自我):
super(B,self).do()
>>>C(A)类:
def do(自我):
super(键入(self),self.do()
>>>D1=B
>>>D2=C
>>>D1().do()
A班
>>>D2().do()
A班
>>>B(A)类:
def do(自我):
打印“新B类”
>>>D1().do()
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
D1().do()
文件“”,第3行,在do中
super(B,self).do()
TypeError:super(type,obj):obj必须是类型的实例或子类型
>>>C(A)类:
def do(自我):
打印“在新的C类中”
>>>D2().do()
A班
根据@donquestion的建议,我将python版本放在这里:sys.version=2.7.2+(默认值,2011年10月4日,20:06:09)[GCC 4.6.1]

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

名称
TestC
只会在元类完成其工作后绑定到新类,也就是从它的
\uuuuu init\uuuu
方法返回后(在
\uuu init\uuuu
之前,
\uu new\uuuuu
方法)

当我们使用“super”调用将类名指定为第一个参数时,类名并没有神奇地出现在那里:它是一个(模块)全局变量,类本身被指定给它-在正常情况下

在本例中,名称尚未分配-但是,由于它是一个classmethod,因此在
cls
变量中有对类的引用-这就是它工作的原因

如果某段代码采用了动态创建派生类之类的技巧,这一点很重要——如果将类名指定给类本身以外的另一个对象,则不应将该类名用作Super的第一个参数。相反,对于类方法,
cls
,或者对于实例方法,
self.\uuuuu类
可以传递给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
>>> 
>>A类(对象):
...   def do(自我):
...      打印“A类”
... 
>>>B(A)类:
...   def do(自我):
...     super(B,self).do()
... 
>>>C=B
>>>C().do()
A班
>>>B类(对象):
...   def do(自我):
...      打印“新B类”
... 
>>>C().do()
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第3行,在do中
TypeError:super(type,obj):obj必须是类型的实例或子类型
>>> 

@Don Question:“您的元类testMetaA实际上并没有构建一个类。”这样做的话,您可以尝试将函数包装在
\uuuu init\uuuu
中。如果不重写元类
\uuuuu new\uuuu
,并不意味着它将不使用超级元类的
\uuuu new\uuuu
。你可以在网上找到很多例子。您应该注意到,与普通类一样,元类的
\uuuu new\uuuuu
的第一个参数与元类的
\uuuuu init\uuuuu
的第一个参数完全不同。除了要修改类名或基元组之外,您可以始终坚持使用
\uuuu init\uuu
。我认为它比
\uuuu new\uuuu
更方便,因为在
\uuuu init\uuuu
之后的
\uuuuu new\uuuu
中,元类的实例cls实际上存在。当然,在
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。然后在
\uuuu new\uuuu
中修改它。对我来说,
self
实际上是指一个普通类的实例。我通常使用
meta
作为元类
\uuuu new\uuuu
的第一个参数,
cls
引用元类的实例(这是一个普通类)。你想用什么词都行,没关系。无论如何,这与我的问题完全无关。@Don质问:“啊,你把你的元类放在方法定义下了”;这实际上无关紧要,您可以将位置更改为def类的第一行。这不会影响任何事情。Don问:对不起,你关于元类如何工作的评论是错误的。我觉得他们在这里给上下文增添了混乱。非常清楚地说:(1)元类不需要指定