这个问题是关于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还需要额外的内存成本吗?或者,这是一个留给实现的选择吗?
发布于 2013-02-21 00:52:48
Dog和sparky都是由构成模块的全局名称空间引用的,它们都保存在内存中。
如果你要运行del Dog,sparky仍然会引用这个类(通过它的__class__引用),让它保持活动状态。该类引用了两个属性,这两个属性是它的定义的一部分,因此它们也会保持活动状态。这完全独立于get_dog_info函数。
CPython根据引用计数将对象保存在内存中;如果Python中的任何内容开始引用某个对象,该对象的引用计数将增加1,当引用被删除时,该对象的引用计数再次减少。当计数降到0时,对象将从内存中移除,垃圾收集进程会根据需要分解循环引用,以促进此过程。
请注意,因为sparky是全局变量,所以函数代码不直接引用任何内容;全局变量是在运行时查找的。如果你也要删除sparky,所有的引用都会被清除。由于get_dog_info()中的sparky是在全局名称空间中查找的,因此调用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'发布于 2013-02-21 04:57:01
作为对Martijn's answer的补充,我将添加以下内容,说明为什么Dog对象(sparky)存储在闭包中,而不是字符串(sparky.breed),我认为这至少是您问题的一部分。
这是因为.操作符的工作方式--它在函数调用时访问sparky的breed属性,因此必须存储整个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()...you可以看到函数的闭包包含一个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字符串,您必须单独引用它:
>>> def get_dog_info_closure():
... sparky = Dog()
... sbreed = sparky.breed
... def get_dog_info():
... return sbreed
... return get_dog_info
...
>>> get_dog_info = get_dog_info_closure()
>>> get_dog_info.func_closure[0].cell_contents
'electronic dog'发布于 2013-02-21 00:48:32
显然,在您向我们展示的代码中,根本没有闭包(由于全局变量)。我假设它只是一个片段。请看下面的代码(作为示例):
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可以是一个属性)。因此,没有地方可以改进这一点。
https://stackoverflow.com/questions/14985133
复制相似问题