ovg*_*vin 6 python recursion class decorator
最近我读了一篇关于如何在Python中创建单例的有趣讨论.其中一个解决方案是一个棘手的装饰器,在其代码中定义一个类作为装饰类的替代:
def singleton(class_):
class class_w(class_):
_instance = None
def __new__(class2, *args, **kwargs):
if class_w._instance is None:
class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs)
class_w._instance._sealed = False
return class_w._instance
def __init__(self, *args, **kwargs):
if self._sealed:
return
super(class_w, self).__init__(*args, **kwargs)
self._sealed = True
class_w.__name__ = class_.__name__
return class_w
@singleton
class MyClass(object):
def __init__(self, text):
print text
@classmethod
def name(class_):
print class_.__name__
x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)
Run Code Online (Sandbox Code Playgroud)
输出是:
111 # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True # this is actually the same instance
Run Code Online (Sandbox Code Playgroud)
据说,如果我们用super(MyClass, self).__init__(text)里面__init__的MyClass,我们进入递归.
我测试了,确实发生了递归.但是,据我所知,MyClass继承object,所以super(MyClass, self)应该只是object,但事实证明super(MyClass, self)是__main__.MyClass
你能解释一下这里发生了什么,让我理解递归发生的原因吗?
问题在于,通过写作super(MyClass, self).__init__(text),你说要使用超级相对于MyClass当时所指的任何类super.但是装饰器取代MyClass了它自己的子类.因此,当__init__调用原始方法时,MyClass实际上是指定义执行方法的类的子类.
要一步一步说出来,我将调用原始类(如源代码中所写)OrigMyClass和结果版本(在装饰器之后)DecMyClass.我将MyClass仅用作变量,因为它的含义在执行过程中会发生变化.
您可以定义一个__init__方法OrigMyClass,但该__init__方法super(MyClass, self)不会调用super(OrigMyClass, self).因此,实际调用什么方法取决于调用方法时MyClass引用的内容.MyClass像任何其他变量一样,查找执行时的值; 将它放在super调用内或__init__方法内部并不会神奇地将它绑定到它写入时恰好出现的类; 函数中的变量在调用时进行评估,而不是在定义它们时进行评估.
装饰者运行.装饰器将新类定义DecMyClass为子类OrigMyClass. DecMyClass定义一个__init__调用super(DecMyClass, self).
装饰器运行后,名称MyClass绑定到类DecMyClass.请注意,这意味着当super(MyClass, self)稍后执行调用时,它将执行super(DecMyClass, self).
当你这样做时MyClass(111),你实例化一个对象DecMyClass. DecMyClass.__init__电话super(DecMyClass, self).__init__.这执行OrigMyClass.__init__.
OrigMyClass.__init__电话super(MyClass, self).__init__.因为MyClass指DecMyClass,这是一样的super(DecMyClass, self).__init__.但是DecMyClass是它的子类OrigMyClass.关键点是因为MyClass指的是DecMyClass,OrigMyClass实际上是在自己的子类上调用super .
因此,super(DecMyClass, self).__init__再次呼叫OrigMyClass.__init__,再次呼唤自己,等等到无限.
效果与此代码相同,这可能会使执行路径更加明显:
>>> class Super(object):
... def __init__(self):
... print "In super init"
... super(Sub, self).__init__()
>>> class Sub(Super):
... def __init__(self):
... print "In sub init"
... super(Sub, self).__init__()
Run Code Online (Sandbox Code Playgroud)
请注意Super电话super(Sub, self).它试图调用超类方法,但它试图调用超类方法Sub.超类Sub是Super,所以Super再次调用自己的方法.
编辑:只是为了澄清你提出的名称查找问题,这是另一个略有不同的例子,具有相同的结果:
>>> class Super(object):
... def __init__(self):
... print "In super init"
... super(someClass, self).__init__()
>>> class Sub(Super):
... def __init__(self):
... print "In sub init"
... super(Sub, self).__init__()
>>> someClass = Sub
Run Code Online (Sandbox Code Playgroud)
这应该清楚地表明super(第一个参数,这里someClass)的类参数在任何方面都不是特殊的.它只是一个普通的名称,其值在平时以普通方式查找,即super执行调用时.如此示例所示,在定义方法时,变量甚至不必存在; 在调用方法时查找该值.