Python中的闭包?

2024-10-08 19:28:00 发布

您现在位置:Python中文网/ 问答频道 /正文

当我运行这段代码时,我得到的结果是:

15
15

我希望输出应该是

^{pr2}$

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

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)

Tags: andlambda代码makereturndef事实setter
3条回答

python2.x有一个语法限制,不允许在读/写时捕获变量。在

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

  1. 该变量是全局变量,已用global x声明
  2. 变量是函数的局部变量

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

在python3.x中,这已经被添加了nonlocal声明所取代。在python3中,您的代码将按预期工作,方法是

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

    return (lambda y: x + y, setter)

Python2.x运行时能够在字节码级别处理closed over变量的读写操作,但是编译器接受的语法存在局限性。在

您可以看到一个lisp编译器,它直接生成python字节码,它创建一个具有读写捕获状态at the end of this video的加法器闭包。编译器可以为python2.x、python3.x或PyPy生成字节码。在

如果在Python 2.x中需要closed over mutable state,一个窍门是使用一个列表:

^{pr2}$

内部的def setter(n)函数定义了它自己的局部变量x。这将隐藏另一个x变量,该变量是make_adder_and_setter的参数(在作用域中形成一个洞)。因此setter函数没有副作用。它只设置内部局部变量的值并退出。在

如果您尝试下面的代码,可能会对您很清楚。它做的完全一样,只是用了z的名字而不是x

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

    return (lambda y: x + y, setter)

myadder, mysetter = make_adder_and_setter(5)
print myadder(10)
mysetter(7)
print myadder(10)

您正在setter()函数中设置一个局部变量x。对函数中的名称赋值会将其标记为本地名称,除非您特别告诉Python编译器。在

在Python3中,可以使用nonlocal关键字显式地将x标记为非本地的:

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

    return (lambda y: x + y, setter)

现在,x被标记为一个自由变量,并在分配给时在周围的范围中查找。在

在python2中,不能将Python标记为本地的。唯一的另一个选项是将x标记为global。您将不得不使用一些技巧来更改位于周围范围内的可变对象所包含的值。在

setter函数上的属性可以工作,例如,settermake_adder_and_setter()作用域的本地属性,任何可以访问setter的对象都可以看到该对象上的属性:

^{pr2}$

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

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

    return (lambda y: x[0] + y, setter)

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

相关问题 更多 >

    热门问题