在python中派生类方法的正确方法是什么?
最近,我遇到了一个元类调用派生类方法的问题。 例如,我得到一个简单的基类testA,它有一个classmethod在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):
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)元类不需要指定