2013-08-16 45 views
4

当我运行这段代码,我得到这样的结果:在python中关闭?

15 
15 

我期望输出应该是

15 
17 

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

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) 
+0

@Wooble:我最终棉花,是的。 :-) –

回答

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) 

蟒蛇2.x的运行时能够处理读 - 写在字节级关闭了变数,但是限制是语法的编译器接受。

你可以看到一个LISP编译器直接生成字节码蟒创建具有读写拍摄状态at the end of this video加法器关闭。编译器可以为Python 2.x,Python 3.x或PyPy生成字节码。

如果您需要封闭在Python中的可变状态2.x的一招就是用一个列表:

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

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

谢谢,现在我明白了这个问题 –

13

您在setter()功能设置本地变量x。赋值给函数中的名称会将其标记为本地名称,除非您明确地告诉Python编译器。

在Python 3,你可以使用关键字nonlocal明确标示x作为非本地:

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

    return (lambda y: x + y, setter) 

现在x被标记为自由变量,而是在分配给在周边范围内抬头。

在Python 2中,您不能标记为Python本地。您唯一的其他选项是将x标记为global。您必须使用技巧来改变位于周围范围内的可变对象所包含的值。例如:

属性setter函数将工作,例如; setter是本地make_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) 

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

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

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

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

2

你内心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)