Python单例模式疑问:装饰器局部变量为何未被销毁
为什么单例装饰器里的局部变量
instances不会被销毁? 这问题问得太戳痛点了!我当初第一次看到这个装饰器实现时,也和你一样满脸问号——按常规逻辑,singleton函数执行完,它的局部变量instances应该被垃圾回收才对,怎么还能一直保存实例状态?核心原因其实是Python里的**闭包(Closure)**特性,咱们慢慢掰扯清楚:
先搞懂闭包的本质
当一个函数(比如这里的getinstance)定义在另一个函数(singleton)内部,并且内部函数引用了外部函数作用域里的变量时,这个内部函数就会形成一个闭包。闭包的关键在于:外部函数执行完毕后,它的作用域并不会被销毁,而是被内部函数保留下来,供内部函数后续调用时使用。
结合你的代码拆解流程
当你用@singleton装饰MyClass时,Python会悄悄做这几件事:
- 执行
singleton(MyClass),此时singleton函数里的instances字典被创建(空字典)。 singleton返回内部函数getinstance,然后Python把原来的MyClass替换成这个getinstance函数——也就是说,现在你写MyClass(),其实是在调用getinstance()。- 重点来了:
getinstance引用了singleton作用域里的instances变量,所以singleton执行完后,它的作用域不会被销毁,instances字典被getinstance“抓着”,存在闭包的环境中。
实例创建的具体逻辑
- 第一次调用
MyClass():getinstance检查instances里有没有MyClass这个键,此时是空字典,所以创建MyClass的实例并存入instances,然后返回这个实例。 - 第二次调用
MyClass():getinstance还是用同一个闭包里的instances字典,此时MyClass已经在字典里了,直接返回已有的实例,不会再创建新的。
举个更简单的闭包例子辅助理解
如果还是有点懵,看这个小例子:
def make_counter(): count = 0 # 外部函数的局部变量 def counter(): nonlocal count count += 1 return count return counter my_counter = make_counter() print(my_counter()) # 输出 1 print(my_counter()) # 输出 2
这里count是make_counter的局部变量,但counter函数引用了它,所以make_counter执行完后,count并没有被销毁,每次调用my_counter都能修改并访问它——这和你单例装饰器里的instances是一个道理!
总结一下
你之前的误解是“普通函数执行完局部变量就销毁”,但闭包打破了这个常规:只要内部函数被外部引用(这里就是装饰后getinstance变成了MyClass,被你后续调用),外部函数的作用域就会被保留,里面的变量就能一直被内部函数使用。所以instances字典才能一直存在,帮你检测是否已有实例。
内容的提问来源于stack exchange,提问作者Anvil
相关产品推荐
相关产品推荐

