Closing in python?

When I run this code, I get this result:

15 15 

I expect the result should be

 15 17 

but this is not so. The question is why?

 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) 
+4
source share
3 answers

Python 2.x has a syntax limitation that prevents the variable from being captured in read / write mode.

The reason is that if a variable is assigned in a function, there are two possibilities:

  • the variable is global and was declared using global x
  • the variable is a local function

more specifically, he ruled out that the variable is the local scope of the closing function

This has been replaced in Python 3.x with the addition of a nonlocal . Your code will work as expected in Python 3, changing it to

 def make_adder_and_setter(x): def setter(n): nonlocal x x = n return (lambda y: x + y, setter) 

The python 2.x script is capable of handling a closed byte-code read and write variable, however, the limitation lies in the syntax that the compiler accepts.

You can see the lisp compiler that generates python bytecode directly, which creates an adder closure with a written read and write state at the end of this video . The compiler can generate bytecode for Python 2.x, Python 3.x, or PyPy.

If you need a private closed permutation state in Python 2.x, you need to use a list:

 def make_adder_and_setter(x): x = [x] def setter(n): x[0] = n return (lambda y: x[0] + y, setter) 
+8
source

The setter() you set the local variable x . Assigning a name to a function designates it as local unless you specifically specify the Python compiler otherwise.

In Python 3, you can explicitly mark x as non-local using the nonlocal keyword:

 def make_adder_and_setter(x): def setter(n): nonlocal x x = n return (lambda y: x + y, setter) 

Now x is marked as a free variable and is viewed in the environment, not at the destination.

In Python 2, you cannot mark local Python as such. The only other option you have is to mark x as global . You have to resort to tricks in which you change the values ​​contained in a mutable object that lives in the environment.

The setter function attribute will work, for example; setter is local to make_adder_and_setter() , the attributes of this object will be visible to everyone with access to setter :

 def make_adder_and_setter(x): def setter(n): setter.x = n setter.x = x return (lambda y: setter.x + y, setter) 

Another trick is to use a mutable container, like a list:

 def make_adder_and_setter(x): x = [x] def setter(n): x[0] = n return (lambda y: x[0] + y, setter) 

In both cases, you no longer assign a local name; the first example uses attribute assignment for the setter object, the second modifies the list x , and is not assigned to x itself.

+13
source

The internal function def setter(n) defines its own local variable x . This hides another variable x , which was the make_adder_and_setter parameter (makes a hole in the area). Thus, the setter function has no side effect. It simply sets the value of the internal local variable and exits.

It may be clear to you if you try the code below. It does the same, just uses the name z instead of 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) 
+2
source

Source: https://habr.com/ru/post/1497432/


All Articles