我尝试通过以下代码了解结束
主
class foo(object):
def __init__(self):
self.a=10
def test(self):
def re():
print self.a
return re
f1=foo()
k1=f1.test()
k1()# output 10
f1.a=20# line A
k1()# output 20
附加case1或case2
情形1:
del f1# line B
k1()# line C output 20
情况2:
del f1.a
k1()# line D .error
从A行看来,k1()输出似乎取决于f1.a.如果我删除了f1,则应删除f1.a。为什么lineC输出20? 。如果k1()不依赖于f1.a,为什么lineD输出错误?请帮忙解释一下。感谢
答案 0 :(得分:2)
说" k1
取决于f1.a
"并不完全准确。 k1
使用表达式self.a
,self.a
不是原子单位,而是由一个裸名称(self)和一个属性(a)组成。其中任何一个都可以独立失败。在第二种情况下,只有属性查找失败(即,对象没有属性a
)。
通过执行del f1
,您没有删除该对象。您只删除了名称 f1
。如果存在对该对象的其他引用,则该对象仍将存在。还有另一个参考,即k1
的结束。有关详细说明,请参阅this question,但基本上在您致电f1.test()
时,该通话的self
值为"已保存"在返回的函数(你称之为k1
)中,因为self
是封闭函数test
的局部变量。因此,k1
仍然可以访问以前称为f1
的对象,并且调用成功。
在第二种情况下,您从名为a
的对象中删除了属性f1
。 re
不保存对该属性的引用,因为闭包只保存封闭函数的局部变量。当您致电re
时,它会尝试查找self.a
,但这会失败,因为虽然该对象存在,但该属性不存在。
有一点可能有助于理解差异:请注意re
未使用名称 f1
。它指的是名称self
。在您的设置中,它最终与f1
成为同一个对象,但该对象有两个不同的名称指向它。相反,re
使用属性名称a
,并且在您的案例2中调用时,该用法会失败。
如果re
使用名称f1
,则删除该名称时也会失败。如果您将re
更改为print f1.a
而不是print self.a
,那么它将查找名为f1
的全局变量,如果该变量不存在则会失败。在这种情况下,它永远不会尝试寻找属性a
,因为该对象首先不存在。
答案 1 :(得分:1)
如果我删除了f1,则应删除f1.a
这不正确。如果您del f1
,它会减少f1
对象的引用计数器,并将其从本地 dict 中删除,以使其无法访问。什么时候参考计数器达到0,Python GC将占用的内存返回给OS。
当您创建访问外部上下文的闭包时,Python会在本地闭包范围内隐式创建对该上下文的引用。在print(locals())
之前添加print self.a
以查看在k1中引用了foo对象。
因此,如果您在现有f1
的案例1中删除k1
,f1
将从模块的上下文中消失,但该对象仍然存在且{{1}中有一个引用}}。所以可以拨打k1
并获得20。
在案例2中k1()
时,它会从del f1.a
对象的上下文中删除a
。只有一个对象f1,f1
无法从a
或f1.a
无法访问。