首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python中的闭包是捕获对象引用,还是只捕获它们所需的属性?

python中的闭包是捕获对象引用,还是只捕获它们所需的属性?
EN

Stack Overflow用户
提问于 2013-02-21 00:38:17
回答 3查看 383关注 0票数 0

这个问题是关于python如何智能地进行逃逸分析。

假设我有以下程序:

代码语言:javascript
复制
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还需要额外的内存成本吗?或者,这是一个留给实现的选择吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-02-21 00:52:48

Dogsparky都是由构成模块的全局名称空间引用的,它们都保存在内存中。

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

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

请注意,因为sparky是全局变量,所以函数代码不直接引用任何内容;全局变量是在运行时查找的。如果你也要删除sparky,所有的引用都会被清除。由于get_dog_info()中的sparky是在全局名称空间中查找的,因此调用get_dog_info()将导致NameError

如果您确实有一个闭包(对父函数作用域中的变量的引用),则将应用相同的规则,只是闭包引用被视为对实例的另一个引用,从而间接地引用到类和包含的属性。

因此,考虑下面的示例,其中我们创建了一个闭包:

代码语言:javascript
复制
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闭包仍然引用该类的一个实例:

代码语言:javascript
复制
>>> bar.__closure__
(<cell at 0x1012b2280: Dog object at 0x1012b5110>,)
>>> bar.__closure__[0].cell_contents
<__main__.Dog object at 0x1012b5110>
>>> bar()
'electronic dog'
票数 4
EN

Stack Overflow用户

发布于 2013-02-21 04:57:01

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

这是因为.操作符的工作方式--它在函数调用时访问sparkybreed属性,因此必须存储整个sparky对象。如果您只想在闭包中存储一个字符串,则必须将函数代码更改为直接引用该字符串。

所以换句话说,考虑到以下情况...

代码语言:javascript
复制
>>> 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返回的字符串:

代码语言:javascript
复制
>>> 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对象并对其进行修改,以后的调用将反映该修改:

代码语言:javascript
复制
>>> get_dog_info.func_closure[0].cell_contents.breed = ('actual '
                                                        'flesh-and-blood dog!')
>>> get_dog_info()
'actual flesh-and-blood dog!'

要只存储breed字符串,您必须单独引用它:

代码语言:javascript
复制
>>> 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'
票数 2
EN

Stack Overflow用户

发布于 2013-02-21 00:48:32

显然,在您向我们展示的代码中,根本没有闭包(由于全局变量)。我假设它只是一个片段。请看下面的代码(作为示例):

代码语言:javascript
复制
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()

这表明整个对象sparkyget_dog_info中已经“关闭”了。事实上,这必须是这样的,因为检索对象的属性需要一些关于对象的知识(例如,breed可以是一个属性)。因此,没有地方可以改进这一点。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14985133

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档