当前位置:  开发笔记 > 编程语言 > 正文

读/写Python闭包

如何解决《读/写Python闭包》经验,为你挑选了6个好方法。

闭包是一种非常有用的语言功能.他们让我们做一些聪明的事情,否则会占用大量代码,并且经常使我们能够编写更优雅,更清晰的代码.在Python 2.x中,闭包变量名不能反弹; 也就是说,在另一个词法范围内定义的函数不能some_var = 'changed!'对其局部范围之外的变量执行某些操作.有人可以解释为什么会这样吗?在某些情况下,我想创建一个在外部作用域中重新绑定变量的闭包,但这是不可能的.我意识到在几乎所有情况下(如果不是全部的话),这种行为可以通过类来实现,但它通常不是那么干净或优雅.为什么我不能用闭包来做?

以下是重新绑定闭包的示例:

def counter():
    count = 0
    def c():
        count += 1
        return count
    return c

这是您调用它时的当前行为:

>>> c()
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 4, in c
UnboundLocalError: local variable 'count' referenced before assignment

我想要它做的是:

>>> c()
1
>>> c()
2
>>> c()
3

sdcvvc.. 29

要扩展Ignacio的答案:

def counter():
    count = 0
    def c():
        nonlocal count
        count += 1
        return count
    return c

x = counter()
print([x(),x(),x()])

在Python 3中给出[1,2,3]; counter()给予独立柜台的调用.其他解决方案 - 尤其是使用itertools/ yield更具惯用性.



1> sdcvvc..:

要扩展Ignacio的答案:

def counter():
    count = 0
    def c():
        nonlocal count
        count += 1
        return count
    return c

x = counter()
print([x(),x(),x()])

在Python 3中给出[1,2,3]; counter()给予独立柜台的调用.其他解决方案 - 尤其是使用itertools/ yield更具惯用性.



2> Chris Lutz..:

你可以这样做,它可以或多或少地以相同的方式工作:

class counter(object):
    def __init__(self, count=0):
        self.count = count
    def __call__(self):
        self.count += 1
        return self.count    

或者,有点黑客:

def counter():
    count = [0]
    def incr(n):
        n[0] += 1
        return n[0]
    return lambda: incr(count)

我会选择第一个解决方案.

编辑:这就是我没有阅读文本的大博客.

无论如何,Python封闭相当有限的原因是"因为Guido感觉就像这样." Python是在90年代早期,在OO的全盛时期设计的.关键词在人们想要的语言功能列表中相当低.随着像头等功能,闭包和其他东西的功能性思想进入主流流行,像Python这样的语言必须加以解决,因此它们的使用可能有点尴尬,因为这不是语言的设计目的.

此外,Python(2.x)还有一些奇怪的(在我看来)关于范围界定的想法,这些想法干扰了一个合理的闭包实现等等.这总是困扰我:

new = [x for x in old]

给我们留下我们x在其使用的范围中定义的名称,因为它(在我看来)是概念上较小的范围.(虽然Python获得了一致性的要点,因为对for循环执行相同的操作具有相同的行为.避免这种情况的唯一方法是使用map.)

无论如何,



3> Ignacio Vazq..:

nonlocal 在3.x应该补救这个.



4> jbochi..:

我会用一台发电机:

>>> def counter():
    count = 0
    while True:
        count += 1
        yield(count)

>>> c = counter()
>>> c.next()
1
>>> c.next()
2
>>> c.next()
3

编辑:我相信你的问题的最终答案是PEP-3104:

在大多数支持嵌套作用域的语言中,代码可以引用或重新绑定(赋值)最近的封闭作用域中的任何名称.目前,Python代码可以引用任何封闭范围中的名称,但它只能重新绑定两个范围中的名称:本地范围(通过简单赋值)或模块全局范围(使用全局声明).

在Python-Dev邮件列表和其他地方已多次提出此限制,并引发了扩展讨论和许多方法来消除此限制.该PEP总结了已经提出的各种替代方案,以及针对每种方案提到的优点和缺点.

在2.1版之前,Python对范围的处理类似于标准C:在文件中只有两个范围,全局和本地.在C中,这是函数定义不能嵌套这一事实的自然结果.但是在Python中,虽然函数通常在顶层定义,但函数定义可以在任何地方执行.这给了Python没有语义的嵌套作用域的语法外观,并产生了令一些程序员感到惊讶的不一致性 - 例如,在顶层工作的递归函数在移动到另一个函数内时将停止工作,因为递归函数是自己的名字将不再在其正文范围内可见.


好吧,Guido是一个人类,人类有时会犯错!-P

5> mipadi..:

函数也可以有属性,所以这也可以工作:

def counter():
    def c():
        while True:
            yield c.count
            c.count += 1
    c.count = 0
    return c

但是,在这个具体的例子中,我会使用jbochi建议的生成器.

至于为什么,我不能肯定地说,但我认为它不是一个明确的设计选择,而是Python有时奇怪的范围规则的残余(尤其是其范围规则的有些奇怪的演变).



6> rob..:

官方Python教程以及Python执行模型对此行为进行了彻底的解释.特别是,从教程:

Python的一个特殊之处在于 - 如果没有全局语句生效 - 对名称的赋值总是进入最内层范围.

但是,这并没有说明为什么它以这种方式表现.

更多信息来自PEP 3104,它试图解决Python 3.0的这种情况.
在那里,您可以看到它是这种方式,因为在某个时间点,它被视为最佳解决方案而不是引入经典的静态嵌套作用域(请参阅Re:作用域(Re:Lambda绑定已解决?)).

那就是说,我也有自己的解释.
Python将名称空间实现为字典; 当一个变量的查找在内部失败时,它会在外部尝试,依此类推,直到它到达内置函数.
但是,绑定变量是一个完全不同的东西,因为你需要指定一个特定的命名空间 - 它总是最内层的(除非你设置"global"标志,这意味着它总是全局命名空间).
最终,用于查找和绑定变量的不同算法是在Python中将闭包设置为只读的原因.
但是,再一次,这只是我的猜测:-)

推荐阅读
N个小灰流_701
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有