说明为什么装饰器只调用一次

时间:2019-03-14 05:39:00

标签: python singleton decorator

我对从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

1 个答案:

答案 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