两者Dog
和sparky
都由构成模块的全局命名空间引用,并将它们保存在内存中。
如果您要运行del Dog
,sparky
仍然会引用该类(通过它的__class__
引用)使其保持活动状态。该类引用了作为其定义一部分的两个属性,因此它们也保持活动状态。这完全独立于get_dog_info
函数。
CPython 根据引用计数将对象保存在内存中;如果 Python 中的任何内容开始在某处引用某个对象,则该对象的引用计数将增加 1,并在删除引用时再次减少。当计数下降到 0 时,对象会从内存中删除,并且垃圾收集过程会根据需要分解循环引用以促进该过程。
注意因为sparky
是全局的,所以函数代码不直接引用任何东西;在运行时查找全局变量。如果您也删除sparky
,所有引用都将被清除。因为sparky
inget_dog_info()
是在全局命名空间中查找的,所以调用get_dog_info()
会产生一个NameError
.
如果您确实有一个闭包(对父函数范围中的变量的引用),则将适用相同的规则,除了闭包引用计为对实例的另一个引用,因此间接地指向类和包含的属性。
因此,考虑以下示例,我们确实在其中创建了一个闭包:
class Dog():
breed = 'electronic dog'
collar_type = 'microsoft'
def foo():
sparky = Dog()
def bar():
return sparky.breed
return bar
bar = foo()
del Dog
在上面的示例中,Dog
该类保留在内存中,因为bar
闭包仍然引用该类的一个实例:
>>> bar.__closure__
(<cell at 0x1012b2280: Dog object at 0x1012b5110>,)
>>> bar.__closure__[0].cell_contents
<__main__.Dog object at 0x1012b5110>
>>> bar()
'electronic dog'