Closure examples in Python

  • 2020-04-02 14:03:11
  • OfStack

The concept of closures is generally covered in many languages, and this article focuses on the definition and use of closures in python. Closures are used in Python primarily for functional development. The detailed analysis is as follows:

A, definitions,

In python closure from the expression formally defined (explanation) : as if in an internal function, on the outside the scope of variables (but not in the global scope) for reference, then the internal function is considered to be a closure (closure). This definition is relatively straightforward, good understanding, not like other definitions pedant flavour is dye-in-the-wood, the pedant taste heavy explain, in the process of interpretation of a noun and filled with a bunch of other strange maddening noun, not suitable for beginners). Here is a simple example to illustrate.


>>>def addx(x): 
>>> def adder(y): return x + y 
>>> return adder 
>>> c = addx(8) 
>>> type(c) 
<type 'function'> 
>>> c.__name__ 
'adder' 
>>> c(10) 
18

Combine this simple code and definition to illustrate closures:
If in an inner function, adder of y is the inner function,
References variables in the external scope (but not in the global scope) : x is the referenced variable, x is in the external scope addx, but not in the global scope,
Then the inner function adder is a closure.

A slightly more elaborate explanation is that the closure is equal to the block + the environment in which the function is defined, and adder is the block, and x is the environment, and of course there can be many, many more than one simple x.

Ii. Precautions for using closures

1. You cannot modify local variables in an external scope in a closure


>>> def foo(): 
...  m = 0 
...  def foo1(): 
...   m = 1 
...   print m 
... 
...  print m 
...  foo1() 
...  print m 
...
>>> foo()
0
1
0

As you can see from the execution, although a variable m is also defined in the closure, it does not change the local variable m in the external function.

2. The following code is a classic error code when using closures in python


def foo(): 
 a = 1 
 def bar(): 
  a = a + 1 
  return a 
 return bar

The intent of this program is to increment the variable a every time the closure function is called. But in practice


>>> c = foo() 
>>> print c() 
Traceback (most recent call last): 
 File "<stdin>", line 1, in <module> 
 File "<stdin>", line 4, in bar 
UnboundLocalError: local variable 'a' referenced before assignment 

This is because when executing code c = foo(), python imports all the closure function body bar() to analyze its local variables. Python rules specify that all variables on the left of the assignment statement are local variables. Then, in the closure bar(), variable a is on the left of the assignment symbol "=" and is considered by python to be a local variable in bar(). Next, when print c() is executed, the program runs until a = a + 1. Since a has been previously classified as a local variable in bar(), python will find the value of a on the right side of the assignment statement in bar(). The solution is simple


def foo(): 
 a = [1] 
 def bar(): 
  a[0] = a[0] + 1 
  return a[0] 
 return bar

Just set a as a container. This is somewhat unsatisfying to use, so after python3, before a = a + 1, use the statement nonlocal a, which explicitly specifies that a is not a local variable of the closure.

3. There is another example that is often mentioned in the introduction of python closures. I have never felt that this error has much to do with closures, but it is an error that is easy to make in python functional programming. Let's look at this code first


for i in range(3): 
 print i

The problem with Python is that when the loop ends, the temporary variable I in the body of the loop is not destroyed, but continues to exist in the execution environment. Another python phenomenon is that python functions only find the value of a variable in the body of the function when it is executed.


flist = [] 
for i in range(3): 
 def foo(x): print x + i 
 flist.append(foo) 
for f in flist: 
 f(2)

Some might think that this code should be 2,3,4. But the actual result is 4,4,4. This is because when the function is added to the list of flist, python has not yet assigned a value to I, only when it is executed, to find out what the value of I is, and then after the first for loop, the value of I is 2, so the result of the above code is 4,4,4.
The solution is simple, just rewrite the definition of the function.


for i in range(3): 
 def foo(x,y=i): print x + y 
 flist.append(foo) 

Three,

With all that said, it's tempting to ask, how useful is this closure in real development? Closures are primarily used during functional development. The two main USES of closures are described below.

Purpose 1: when the closure is completed, the current runtime environment can still be maintained.

For example, if you want every execution of a function to be based on the last run of the function. Let me give you an example like a board game. Suppose the board size is 50*50, and the top left corner is the origin of the coordinate system (0,0). I need a function that takes two parameters, namely direction and step, which controls the movement of the pieces. In addition to relying on the direction and step size, the new coordinates of the movement of the chess piece also depend on the original coordinate point, which can be maintained by using the closure.


origin = [0, 0] #  Origin of coordinate system  
legal_x = [0, 50] # x The legal coordinates of the axis direction  
legal_y = [0, 50] # y The legal coordinates of the axis direction  
def create(pos=origin): 
 def player(direction,step): 
  #  The parameters should be determined first direction,step Legitimacy, for example direction You can't walk sideways, step It can't be negative  
  #  And then we're going to do the new one x . y The validity of the coordinate is judged. The main purpose here is to introduce closures, so I won't go into details.  
  new_x = pos[0] + direction[0]*step 
  new_y = pos[1] + direction[1]*step 
  pos[0] = new_x 
  pos[1] = new_y 
  # Attention! I can't write it here  pos = [new_x, new_y] For the reasons mentioned above  
  return pos 
 return player 
 
player = create() #  Create pieces player , the starting point is the origin  
print player([1,0],10) #  to x It's moving in the positive direction 10 step  
print player([0,1],20) #  to y It's moving in the positive direction 20 step  
print player([-1,0],10) #  to x It's moving in the negative direction 10 step  

The output is:


[10, 0] 
[10, 20] 
[0, 20] 

Purpose 2: closures can get different results based on local variables in the external scope, which is a bit like a configuration function where we can modify external variables, and the closure can behave differently depending on the variable. For example, sometimes we need to analyze the special lines of some files and extract the special lines first.


def make_filter(keep): 
 def the_filter(file_name): 
  file = open(file_name) 
  lines = file.readlines() 
  file.close() 
  filter_doc = [i for i in lines if keep in i] 
  return filter_doc 
 return the_filter

If we need to get the line in the file "result.txt" that contains the "pass" keyword, we can use the example program in this way


filter = make_filter("pass")
filter_result = filter("result.txt")

The above two usage scenarios can be easily implemented with object orientation, but in functional programming with Python, closures are useful for persisting data and generating different capabilities by configuration.

I believe that this article has some reference value for your Python programming.


Related articles: