A detailed description of closures and decorators in Python

Source: Internet
Author: User

Closure is an important syntax structure for functional programming. Closure is also a structure for organizing code, which also improves code reusability.

If you reference a variable in an external function (but not in the global scope) in an embedded function, the nested function is considered as a closure ).

Variables defined in external functions but referenced or used by internal functions are called free variables.

To sum up, creating a closure must meet the following requirements:

1. An embedded function is required.
2. Embedded functions must reference variables in external functions.
3. The returned value of an external function must be an embedded function.
### 1. Closure example

Let's look at an example of a closure:

In [10]: def func (name ):
...: Def in_func (age ):
...: Print 'name: ', name, 'Age:', age
...: Return in_func
...:
   
In [11]: demo = func ('feiyu ')
   
In [12]: demo (19)
Name: feiyu age: 19
Here, when func is called, a closure -- in_func is generated, and the closure holds the free variable -- name. Therefore, this means that after the lifecycle of the function func ends, the name variable still exists because it is referenced by the closure and will not be recycled.

In a python function, you can directly reference external variables, but cannot rewrite external variables. Therefore, if you directly rewrite the variables of the parent function in the closure, an error will occur. See the following example:

An example of implementing a counting closure:

Def counter (start = 0 ):
Count = [start]
Def incr ():
Count [0] + = 1
Return count
Return incr
    
A = counter ()
Print 'A: ',
 
In [32]: def counter (start = 0 ):
...: Count = start
...: Def incr ():
...: Count + = 1
...: Return count
...: Return incr
...:
   
In [33]: a = counter ()
   
In [35]: a () # An error is reported here.
   
UnboundLocalError: local variable 'count' referenced before assignment
It should be used as follows:

In [36]: def counter (start = 0 ):
...: Count = [start]
...: Def incr ():
...: Count [0] + = 1
...: Return count
...: Return incr
...:
   
In [37]: count = counter (5)
   
In [38]: for I in range (10 ):
...: Print count (),
...:
[6] [7] [8] [9] [10] [11] [12] [13] [14] [15]
### 2. Trap of using closures

In [1]: def create ():
...: Return [lambda x: I * x for I in range (5)] # Generate a list of anonymous functions by derivation
...:
   
In [2]: create ()
Out [2]:
[<Function _ main _. <lambda>,
<Function _ main _. <lambda>,
<Function _ main _. <lambda>,
<Function _ main _. <lambda>,
<Function _ main _. <lambda>]
   
In [4]: for mul in create ():
...: Print mul (2)
...:
    8
    8
    8
    8
    8
The result is not very strange. This is a trap in the use of closures! To see why?

In the code above, the function create returns a list containing four function variables. These four functions reference the circular variable I together, that is, they share the same variable I, I will change. When the function is called, the cyclic variable I is already equal to 4, so the four functions return 8. If you need to use the value of the loop variable in the closure, use the loop variable as the default parameter of the closure or use the partial function. The implementation principle is also very simple, that is, when the cyclic variable is passed into the function, a new memory will be applied. The sample code is as follows:

In [5]: def create ():
...: Return [lambda x, I = I * x for I in range (5)]
...:
In [7]: for mul in create ():
...: Print mul (2)
...:
    0
    2
    4
    6
    8
3. Closure and decorator
The decorator is a closure application, but it passes functions:

   
Def addb (func ):
Def wrapper ():
Return '<B>' + func () + '</B>'
Return wrapper
   
Def addli (func ):
Def wrapper ():
Return '<li>' + func () + '</li>'
Return wrapper
   
@ Addb # equivalent to demo = addb (addli (demo ))
@ Addli # equivalent to demo = addli (demo)
Def demo ():
Return 'Hello world'
   
Print demo () # The execution is addb (addku (demo ))
During execution, the demo function is first passed to addli for decoration, and then the decorated function is passed to addb for decoration. So the final returned result is:

<B> <li> hello world </li> </B>
4. Traps in the decorator
When you write a decorator for a function, the important metadata of the function, such as the name, document string, annotation, and parameter signature, will be lost.

Def out_func (func ):
Def wrapper ():
Func ()
Return wrapper
   
@ Out_func
Def demo ():
"""
This is a demo.
"""
Print 'Hello world .'
   
If _ name _ = '_ main __':
Demo ()
Print "_ name __:", demo. _ name __
Print "_ doc __:", demo. _ doc __
Result:

Hello world.
_ Name __: wrapper
_ Doc __: None
Both the function name and document string are converted into closure information. Fortunately, you can use the @ wraps modifier in the functools library to annotate underlying packaging functions.

From functools import wraps
   
Def out_func (func ):
@ Wraps (func)
Def wrapper ():
Func ()
Return wrapper
Try the results by yourself!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.