这个问题是关于python如何智能地进行逃逸分析。
假设我有以下计划:
class Dog():
breed = 'electronic dog'
collar_type = 'microsoft'
sparky=Dog()
def get_dog_info():
return sparky.breed
函数get_dog_info()显然必须关闭sparky.breed。但是,为此,实现是否也会逃避整个Dog对象?也就是说,关闭collar_type会有额外的内存成本吗?或者这是一个留给实施的选择?
答案 0 :(得分:3)
构成模块的全局命名空间引用Dog
和sparky
,并将它们保存在内存中。
如果您要运行del Dog
,sparky
仍然会引用该类(通过它的__class__
引用)来保持它的存活。该类引用了属于它定义的两个属性,因此它们也保持活着。这是get_dog_info
函数的所有独立。
CPython根据引用计数将对象保存在内存中;如果Python中的任何内容开始引用对象 somethere ,则该对象的引用计数增加1,并在删除引用时再次减少。当计数降至0时,对象将从内存中删除,并且垃圾收集过程会根据需要拆分循环引用以促进此过程。
请注意,因为sparky
是全局的,所以函数代码不会直接引用任何内容;在运行时查找全局变量。如果您也要删除sparky
,则会清除所有引用。由于sparky
中的get_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'
答案 1 :(得分:1)
显然,在您向我们展示的代码中,根本没有闭包(由于全局变量)。我认为这只是一个片段。看一下这段代码(作为例子):
def test():
class Dog():
breed = 'electronic dog'
collar_type = 'microsoft'
sparky=Dog()
def get_dog_info():
return sparky.breed
print get_dog_info.func_closure
test()
表示整个对象sparky
已在get_dog_info
中“关闭”。事实上,这必须是这样的,因为检索对象的属性需要一些关于对象的知识(breed
可以是一个属性)。所以没有地方可以改善它。
答案 2 :(得分:1)
作为Martijn's answer的补充,我将添加以下内容,说明为什么Dog
对象(sparky
)存储在闭包中而不是字符串(sparky.breed
),我认为这至少是你问题的一部分。
这是因为.
运算符的工作方式 - 它在函数调用时访问breed
的{{1}}属性,因此整个sparky
必须存储对象。如果只想在闭包中存储一个字符串,则必须更改函数代码以直接引用该字符串。
换句话说,给出以下内容......
sparky
...你可以看到函数的闭包含一个>>> class Dog():
... breed = 'electronic dog'
... collar_type = 'microsoft'
...
>>> def get_dog_info_closure():
... sparky = Dog()
... def get_dog_info():
... return sparky.breed
... return get_dog_info
>>> get_dog_info = get_dog_info_closure()
对象,而不仅仅是Dog
返回的字符串:
sparky.breed
这意味着您可以检索>>> get_dog_info.func_closure
(<cell at 0x10049fa28: instance object at 0x1004a1cf8>,)
>>> get_dog_info.func_closure[0].cell_contents
<__main__.Dog instance at 0x1004a1cf8>
对象并对其进行修改,以后的调用将反映该修改:
Dog
要仅存储>>> get_dog_info.func_closure[0].cell_contents.breed = ('actual '
'flesh-and-blood dog!')
>>> get_dog_info()
'actual flesh-and-blood dog!'
字符串,您必须单独引用它:
breed