0

这个问题是关于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 是否也会产生额外的内存成本?还是这是由实施决定的选择?

4

3 回答 3

4

两者Dogsparky都由构成模块的全局命名空间引用,并将它们保存在内存中。

如果您要运行del Dogsparky仍然会引用该类(通过它的__class__引用)使其保持活动状态。该类引用了作为其定义一部分的两个属性,因此它们也保持活动状态。这完全独立get_dog_info函数。

CPython 根据引用计数将对象保存在内存中;如果 Python 中的任何内容开始在某处引用某个对象,则该对象的引用计数将增加 1,并在删除引用时再次减少。当计数下降到 0 时,对象会从内存中删除,并且垃圾收集过程会根据需要分解循环引用以促进该过程。

注意因为sparky是全局的,所以函数代码不直接引用任何东西;在运行时查找全局变量。如果您也删除sparky,所有引用都将被清除。因为sparkyinget_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'
于 2013-02-20T16:52:47.610 回答
2

作为对Martijn 回答的补充,我将添加以下内容,说明为什么将Dog对象 ( sparky) 存储在闭包中而不是字符串 ( sparky.breed) 中,我认为这至少是您问题的一部分。

这是因为.操作符的工作方式——它在函数调用时访问 的breed属性,因此必须存储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字符串,您必须单独引用它:

>>> 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-20T20:57:01.307 回答
1

显然,在您向我们展示的代码中,根本没有闭包(由于 globals )。我认为这只是一个片段。看看这段代码(例如):

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例如可以是属性)。所以没有地方可以改进。

于 2013-02-20T16:48:32.323 回答