说明为什么装饰器只调用一次 [英] Clarification on why decorator only called once

查看:122
本文介绍了说明为什么装饰器只调用一次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对于从此处获得的代码感到困惑:

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


推荐答案


为什么每次实例化该类时, wrapper_singleton.instance = None 都将实例设置为none

Why doesn't wrapper_singleton.instance = None set the instance to none each time the class is instantiated?

因为部分代码仅在装饰类时执行。

这是

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 ,它的作用就像是包装的可调用对象,如 @smarie 此处很好地解释了。

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

这篇关于说明为什么装饰器只调用一次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆