我对从here获得的这段代码感到困惑:
import functools
def singleton(cls):
"""Make a class a Singleton class (only one instance)"""
@functools.wraps(cls)
def wrapper_singleton(*args, **kwargs):
if not wrapper_singleton.instance:
wrapper_singleton.instance = cls(*args, **kwargs)
return wrapper_singleton.instance
print('****')
wrapper_singleton.instance = None
return wrapper_singleton
@singleton
class TheOne:
pass
为什么每次实例化类时wrapper_singleton.instance = None
都没有将实例设置为none?我在该行的上方放置了一条打印语句,它也仅被调用一次。谢谢
>>> first_one = TheOne()
>>> another_one = TheOne()
>>> id(first_one)
140094218762280
>>> id(another_one)
140094218762280
>>> first_one is another_one
True
答案 0 :(得分:1)
为什么不
wrapper_singleton.instance = None
将该实例设置为无 每次实例化该类?
因为部分代码仅在装饰类时执行。
这个:
@singleton
class TheOne:
pass
在功能上等同于
class TheOne:
pass
TheOne = singleton(TheOne)
两个版本的代码实际上都通过functools.wraps
的魔术返回了一个函数,就好像它是包装的可调用对象一样,正如@smarie很好地解释了here一样。
TheOne = singleton(TheOne)
print(TheOne)
# <function TheOne at 0x00000000029C4400>
如果您移除@functools.wraps
装饰,那么幕后的景象就会变得肤浅:
def singleton(cls)
#@functools.wraps(cls)
def wrapper_singleton(*args, **kwargs): ...
TheOne = singleton(TheOne)
print(TheOne)
# <function singleton.<locals>.wrapper_singleton at 0x00000000029F4400>
因此,TheOne
的名称实际上已分配给wrapper_singleton
函数的内部函数singleton
。
因此,当您执行TheOne()
时,您不会直接实例化该类,而是调用wrapper_singleton
来为您完成该操作。
这意味着仅在装饰类或通过singleton
手动进行装饰时才调用函数TheOne = singleton(TheOne)
。它定义wrapper_singleton
,在其上创建一个附加属性instance
(这样if not wrapper_singleton.instance
不会引发AttributeError),然后以名称TheOne
返回。
您可以通过再次装饰类来破坏单例。
class TheOne:
def __init__(self, arg):
self.arg = arg
TheOne = singleton(TheOne)
t1 = TheOne(42)
print(t1.arg, id(t1))
# 42 43808640
# Since this time around TheOne already is wrapper_singleton, wrapped by functools.wraps,
# You have to access your class object through the __wrapped__ attribute
TheOne = singleton(TheOne.__wrapped__)
t2 = TheOne(21)
print(t2.arg, id(t2))
# 21 43808920