澄清为什么装饰器只调用一次
Clarification on why decorator only called once
我对从 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
为什么每次实例化 class 时,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
Why doesn't wrapper_singleton.instance = None
set the instance to none
each time the class is instantiated?
因为那部分代码只在 class 修饰时执行。
这个:
@singleton
class TheOne:
pass
在功能上等同于
class TheOne:
pass
TheOne = singleton(TheOne)
两个版本的代码实际上 return 通过 functools.wraps
的魔法实现的函数,它的行为就好像它是包装的可调用对象一样,如 @smarie excellently explains 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
实际上分配给了 singleton
函数的内部函数 wrapper_singleton
。
因此,当您执行 TheOne()
时,您不会直接实例化 class,而是调用 wrapper_singleton
来为您执行此操作。
这意味着,函数 singleton
仅在您装饰 class 或通过 TheOne = singleton(TheOne)
手动完成时调用。它定义了 wrapper_singleton
,在其上创建了一个附加属性 instance
(这样 if not wrapper_singleton.instance
就不会引发 AttributeError),然后 return 将其命名为 TheOne
.
你可以通过再次装饰 class 来打破单例。
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
我对从 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
为什么每次实例化 class 时,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
Why doesn't
wrapper_singleton.instance = None
set the instance to none each time the class is instantiated?
因为那部分代码只在 class 修饰时执行。
这个:
@singleton
class TheOne:
pass
在功能上等同于
class TheOne:
pass
TheOne = singleton(TheOne)
两个版本的代码实际上 return 通过 functools.wraps
的魔法实现的函数,它的行为就好像它是包装的可调用对象一样,如 @smarie excellently explains 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
实际上分配给了 singleton
函数的内部函数 wrapper_singleton
。
因此,当您执行 TheOne()
时,您不会直接实例化 class,而是调用 wrapper_singleton
来为您执行此操作。
这意味着,函数 singleton
仅在您装饰 class 或通过 TheOne = singleton(TheOne)
手动完成时调用。它定义了 wrapper_singleton
,在其上创建了一个附加属性 instance
(这样 if not wrapper_singleton.instance
就不会引发 AttributeError),然后 return 将其命名为 TheOne
.
你可以通过再次装饰 class 来打破单例。
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