如何用类装饰器实现Python单例模式?为何手动调用装饰器导致实例不唯一
两种类装饰器写法的核心差异
@装饰器语法和手动调用装饰器等价的前提,是装饰器仅被调用一次,返回的结果被固定赋值给对应变量。测试中的手动写法没有满足这个前提,因此单例完全失效,两种写法并不等价。
@singleton修饰A类的执行逻辑
- 类A定义完成后,Python 会自动执行等价于
A = singleton(A)的操作,全程仅触发1次装饰器外层函数的调用 - 这次调用会把singleton返回的内层函数
_singleton赋值给全局变量A,后续所有A()的实例化操作,本质都是调用这同一个固定的_singleton函数 - 这个函数的闭包会持有唯一的局部字典
_instance,第一次调用时存入A的实例,后续调用直接复用,因此单例逻辑正常生效
手动调用singleton处理B类的失效原因
测试中的b1 = singleton(B)()、b2 = singleton(B)()是两次完全独立的装饰器调用,每一步的执行逻辑为:
- 第一次执行
singleton(B):在函数局部作用域新建一个空的_instance字典,返回一个全新的_singleton内层函数;紧接着调用这个内层函数,在它的闭包里存入B的第一个实例,返回给b1 - 第二次执行
singleton(B):重新完整运行一遍外层singleton函数,再新建一个完全独立的空_instance字典,返回另一个全新的_singleton内层函数。这个新的闭包从未存储过之前B的实例,因此会重新调用B的构造方法生成全新实例,返回给b2
核心问题:两次手动调用拿到的
_singleton根本不是同一个函数对象,各自闭包存储的_instance完全独立,根本不存在全局共享的实例缓存,自然无法实现单例效果。
正确的手动使用方式
如果不使用@语法糖给B类加单例,必须保证装饰器仅运行一次,把返回的实例化入口固定下来:
# 仅调用一次装饰器,把返回的实例化入口赋值给固定变量 SingletonB = singleton(B) b1 = SingletonB() b2 = SingletonB() # 此时两次实例化会拿到完全相同的对象,单例逻辑生效
内容的提问来源于stack exchange,提问作者xiuxing
相关产品推荐
相关产品推荐

