单例模式与WeakValueDictionary

从{}到WeakValueDictionary

今天在看关于面试的题,用装饰器实现的时候用的是普通的字典:

from functools import wraps


def single_ton(cls):
    _instance = {}

    @wraps(cls)
    def single(*args, **kwargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)
        return _instance[cls]
    return single


@single_ton
class SingleTon(object):
    val = 123

    def __init__(self, a):
        self.a = a

if __name__ == '__main__':
    s = SingleTon(1)
    t = SingleTon(2)
    print s is t
    print s.a, t.a
    print s.val, t.val

我突然想到之前不是学到 weakref 库里面有个弱引用的字典,这里不是正好可以用到吗,于是我改了下代码:

from weakref import WeakValueDictionary
def single_ton(cls):
    _instance = WeakValueDictionary()
    ...

很快啊,直接收获一个报错在脸上

KeyError: <class '__main__.SingleTon'>

百思不得其解,最后通过《Python weakref (弱引用 ) 教程》解惑,_instance[cls] = cls(*args, **kwargs) 这段代码等号后面创建的类实例并没有任何一个强引用,只有 WeakValueDictionary() 中的一个弱引用,于是触发了垃圾回收机制,所以字典实际上是空的,于是我们后面的 return 语句自然就会报错 KeyError。

所以可以把代码改成这样:

def single_ton(cls):
    _instance = WeakValueDictionary()

    @wraps(cls)
    def single(*args, **kwargs):
        if cls not in _instance:
            cls_instance = cls(*args, **kwargs)
            _instance[cls] = cls_instance
        return _instance[cls]
    return single

或者像《python 设计模式-单例模式》里面调用 setdefault 方法:

def single_ton(cls):
    _instance = WeakValueDictionary()

    @wraps(cls)
    def single(*args, **kwargs):
        return _instance.setdefault(cls, cls(*args, **kwargs))
    return single

最后,直接用{}和用WeakValueDictionary的区别就在于引用机制。用前者时,因为被字典所引用,无法完全销毁掉已经创建的实例,而后者可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值