问题描述
我对于从获得的代码感到困惑:
I'm confused about this code I got from 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吗?我在该行的上方放置了一条打印语句,它也仅被调用一次。谢谢
Why doesn't wrapper_singleton.instance = None
set the instance to none each time the class is instantiated? I put a print statement above this line and it only gets called once also. Thanks
>>> first_one = TheOne()
>>> another_one = TheOne()
>>> id(first_one)
140094218762280
>>> id(another_one)
140094218762280
>>> first_one is another_one
True
推荐答案
因为部分代码仅在装饰类时执行。
这是
Because that part of the code is only executed the when class is decorated.
This:
@singleton
class TheOne:
pass
在功能上等同于
class TheOne:
pass
TheOne = singleton(TheOne)
两个版本的代码实际上都是通过<$的魔术来返回一个函数的c $ c> functools.wraps ,它的作用就像是包装的可调用对象,如在很好地解释了。
Both versions of the code actually return a function through the magic of functools.wraps
, that acts as if it was the wrapped callable, as @smarie excellently explains here.
TheOne = singleton(TheOne)
print(TheOne)
# <function TheOne at 0x00000000029C4400>
如果删除 @ functools.wraps
装饰,您可以在幕后看到一些肤浅的东西:
If you remove the @functools.wraps
decoration, you have a superficial look behind the scenes:
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()
,您无需直接实例化该类,而是调用 wrapper_singleton
可以为您完成此操作。
这意味着,仅在装饰类或通过 TheOne = singleton(TheOne) singleton
$ c>。它定义了 wrapper_singleton
,在其上创建了一个附加属性 instance
(因此,如果不是wrapper_singleton,则。实例
不会引发AttributeError),然后以名称 TheOne
返回它。
So the name TheOne
is actually assigned to the inner function wrapper_singleton
of your singleton
function.
Hence when you do TheOne()
, you don't instantiate the class directly, you call wrapper_singleton
which does that for you.
This means, that the function singleton
is only called when you decorate the class or do that manually via TheOne = singleton(TheOne)
. It defines wrapper_singleton
, creates an additional attribute instance
on it (so that if not wrapper_singleton.instance
doesn't raise an AttributeError) and then returns it under the name TheOne
.
您可以通过再次装饰类来破坏单身人士。
You can break the singleton by decorating the class again.
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
这篇关于说明为什么装饰器只调用一次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!