最近我读了an interesting discussion on how to make a singleton in Python。 其中一个解决方案是tricky decorator defining a class inside its code as a substitute for decorated class:
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)
输出是:
111 # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True # this is actually the same instance
据说,如果我们在super(MyClass, self).__init__(text)
的{{1}}内使用__init__
,我们会进入递归。
我测试了,确实发生了递归。
但是,据我所知,MyClass
继承了MyClass
,因此object
应仅仅是super(MyClass, self)
,但事实证明object
是super(MyClass, self)
你能解释一下这里发生的事情,让我理解递归发生的原因吗?
答案 0 :(得分:5)
问题在于,通过编写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(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__()
请注意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
这应该清楚地表明super
(第一个参数,这里someClass
)的类参数在任何方面都不是特殊的。它只是一个普通的名称,其值在平时以普通方式查找,即执行super
调用时。如此示例所示,在定义方法时,变量甚至不必存在;在您调用方法时查找该值。