Python中被复制引用的局部变量的生命周期及使用合法性问询
Python中被复制引用的局部变量的生命周期及使用合法性问询
核心结论先给到你
可以明确地说:这种用法在Python里是完全合法的,背后是Python基于引用计数的内存管理机制在起作用,和你熟悉的C语言栈变量逻辑有本质区别。
为什么你的实验里局部变量的内存没被销毁?
你有C语言背景,会默认函数栈上的局部变量在函数返回后会被销毁,但Python的变量模型完全不同:
- 在Python中,变量只是对对象的引用,不是内存地址的别名。函数里的
x只是指向列表对象的一个"指针",当你执行b = x或者s.y = x时,其实是把同一个列表对象的引用复制给了外部的变量/属性,这时候该对象的引用计数会加1。 - 当你在函数内执行
del x,只是删除了x这个局部引用,并不会直接销毁对象本身——只有当某个对象的引用计数降到0时,Python的垃圾回收机制才会回收它的内存。 - 所以哪怕函数执行完毕,只要外部还有指向该对象的引用(比如你的全局
b或者实例属性o.y),这个对象就会继续存在,完全可以正常使用。
关于用这种方式减少self.代码的可行性
你想通过这种方式避免写大量self.,思路是可行的,但有几个需要注意的点:
- 可读性问题:其他维护代码的开发者可能会疑惑
o.y的值是从哪里来的,毕竟它不是在类的初始化方法里定义,也不是通过函数返回赋值的,而是在函数内部隐式赋值的,长期来看可能会增加代码理解成本。 - 线程安全问题:如果你的类实例会被多个线程使用,这种隐式修改实例属性的方式可能会带来意料之外的竞态条件,不如显式赋值或者返回值更清晰。
- 变量覆盖风险:如果多个成员函数都用这种方式给同一个实例属性赋值,很容易出现不小心覆盖的情况,调试起来会比较麻烦。
结合你的代码再拆解说明
看你给出的代码示例:
b=[0,0] print('\nIn function:') def f(): global b x=[i for i in range(10)] # here may be a lots of statements to compute x var b=x # end of the above I save the reference of local x var in global var b print('f1: b, id(b), id(x) ==', b, id(b), id(x)) del x # deleting local var print('f2: b, id(b) ==', b, id(b)) print('m1: b, id(b) ==', b, id(b)) f() print('m2: b, id(b) ==', b, id(b)) print('\nIn class:') class c1: def f(s, n): # ----here is a lots of statements to compute x var --- x=n # ------------- s.y=x # end of the above I save the reference of local x var in object var y print('c1: id(x), id(s.y) ==', id(x), id(s.y)) del x # deleting local var print('c2: id(s.y) ==', id(s.y)) o=c1() o.f([1,2,3]) print('m3: id(o.y), o.y ==', id(o.y), o.y)
执行结果里所有对应的id完全一致,就说明b、x、s.y、o.y都是指向同一个对象的引用。哪怕x被删除了,只要其他引用还在,对象就不会被回收。
补充小提示
其实Python的return语句本质上也是返回对象的引用,和你这种赋值给外部变量的逻辑是一样的——并不会复制对象本身,只是传递引用。所以你之前用return的方式和现在的方式,在内存管理层面没有本质区别,只是代码写法不同。
如果你真的想减少self.的书写,可以考虑在函数内部把self赋值给一个短变量,比如:
def f(self, n): s = self s.y = [i for i in range(n)]
这样既保持了代码的可读性,又减少了重复书写,比隐式赋值局部变量引用的方式更稳妥。
备注:内容来源于stack exchange,提问作者László Szilágyi
相关产品推荐
相关产品推荐

