关闭python?

Rac*_*oob 4 python closures python-2.x

当我运行此代码时,我得到这个结果:

15
15
Run Code Online (Sandbox Code Playgroud)

我希望输出应该是

15
17
Run Code Online (Sandbox Code Playgroud)

但事实并非如此.问题是:为什么?

def make_adder_and_setter(x):
    def setter(n):
        x = n

    return (lambda y: x + y, setter)

myadder, mysetter = make_adder_and_setter(5)
print myadder(10)
mysetter(7)
print myadder(10)
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 13

要设置一个局部变量xsetter()函数.除非你特别告诉Python编译器,否则赋值给函数中的名称会将其标记为本地.

在Python 3中,您可以x使用nonlocal关键字明确标记为非本地:

def make_adder_and_setter(x):
    def setter(n):
        nonlocal x
        x = n

    return (lambda y: x + y, setter)
Run Code Online (Sandbox Code Playgroud)

现在x被标记为自由变量,并在分配时在周围范围内查找.

在Python 2中,您无法将Python本地标记为本地.您拥有的唯一其他选项是标记xglobal.您将不得不求助于改变生活在周围范围内的可变对象所包含的值的技巧.

一个属性setter功能会的工作,例如:settermake_adder_and_setter()范围本地,该对象的属性对任何有权访问的对象都是可见的setter:

def make_adder_and_setter(x):
    def setter(n):
        setter.x = n
    setter.x = x

    return (lambda y: setter.x + y, setter)
Run Code Online (Sandbox Code Playgroud)

另一个技巧是使用可变容器,例如列表:

def make_adder_and_setter(x):
    x = [x]
    def setter(n):
        x[0] = n

    return (lambda y: x[0] + y, setter)
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,您都不再分配本地名称; 第一个示例使用对象上的属性赋值setter,第二个示例更改x列表,而不是分配给x自身.


650*_*502 8

Python 2.x具有语法限制,不允许在读/写中捕获变量.

原因是如果在函数中分配变量,则只有两种可能性:

  1. 变量是全局的,并且已经声明了 global x
  2. 变量是函数的局部变量

更具体地说,它排除了变量是封闭函数范围的局部变量

这已经在Python 3.x中被取代并添加了nonlocal声明.通过将代码更改为,您的代码将在Python 3中按预期工作

def make_adder_and_setter(x):
    def setter(n):
        nonlocal x
        x = n

    return (lambda y: x + y, setter)
Run Code Online (Sandbox Code Playgroud)

python 2.x运行时能够在字节码级别处理读写的闭包变量,但是限制在于编译器接受的语法.

您可以看到一个直接生成python字节码的lisp编译器,它会在此视频结尾处创建一个具有读写捕获状态的加法器闭包.编译器可以为Python 2.x,Python 3.x或PyPy生成字节码.

如果你需要在Python 2.xa中使用封闭式可变状态,则使用一个列表:

def make_adder_and_setter(x):
    x = [x]
    def setter(n):
        x[0] = n

    return (lambda y: x[0] + y, setter)
Run Code Online (Sandbox Code Playgroud)