0. Function nesting
Variable name resolution: LEGB principle
Variable name lookup:
-
First, search from local (L);
-
The local scope (E) of def or lambda in the upper level organization is not found;
-
Find from global scope;
-
Search from the built-in module (B) and find the first place;
Nonlocal keyword: if an internal function wants to change the variables of an external function, it needs to add the nonlocal keyword. An example is as follows
def outer(): a = 100 def inner(): nonlocal a b = 200 a += b print('I'm an internal function', a) inner() print(a) outer()
1. Closure
1.1 concept
First, let's take a look at the explanation of closures on Wiki:
In computer science, closures (English: closures), also known as lexical closures or function closures, are functions that reference free variables. The referenced free variable will exist with the function, even if it has left the environment in which it was created. Therefore, there is another saying that a Closure is an entity composed of a function and its related reference environment. Closures can have multiple instances at runtime. Different reference environments and the same function combination can produce different instances.
1.2 conditions
Three conditions for forming closures:
- Nested function
- Internal functions refer to variables of external functions
- Return inner function
-
A simple example is as follows
''' Find the correspondence on the line x of y Value, equation: y = a*x+b ''' def outer(a, b): def inner(x): return a * x + b return inner line = outer(2, 1) print(line(1))
Simple analysis: viewing through breakpoints
- At this time, the line value is: < function outer.. inner at 0x00000000031758b0 >
1.3 closure trap
First look at a piece of code: think about the results yourself
def my_func(*args): fs = [] for i in range(3): def func(): return i * i fs.append(func) return fs fs1, fs2, fs3 = my_func() print(fs1()) print(fs2()) print(fs3())
Will the result be 0 1 4? The result is not 4. Why?
- Because in the function my_ The internally defined function before func returns is not a closure function, but an internally defined function. Of course, the variable defined in the parent function referenced by this internal function is not a free variable, but only a local variable in the current block.
- Before the internally defined function func is actually executed, any change to the local variable j will affect the operation result of the function func
Correct writing:
def my_func(*args): fs = [] for i in range(3): func = lambda _i=i: _i * _i fs.append(func) return fs fs1, fs2, fs3 = my_func() print(fs1()) print(fs2()) print(fs3())
-
Summary: do not refer to any cyclic variables or variables that will change later in the return closure.
-
Reference article:
2. Decorator
Because I haven't learned object-oriented yet, I won't introduce the content related to class decorator here, but only the content of Guangyu function decorator.
Now let's learn about the decorator by simulating the decoration of a new house. You bought a new house (blank house). Now we want to decorate the blank house.
The opening and closing principle is the most basic and important design principle in programming.
Basic introduction:
- A software entity such as classes, modules and functions should be open to extensions (for the provider) and closed to modifications (for the user). Build the framework with abstraction and extend the details with implementation.
- When the software needs to change, try to realize the change by expanding the behavior of the software entity, rather than by modifying the existing code.
- Other principles are followed in programming, and the purpose of using design patterns is to follow the opening and closing principles.
Examples are as follows:
def decorator(func): def wrapper(): func() print('Painting') print('Lay the floor') print('Buy furniture') print('Hardbound repair room, you can check in~~~') return wrapper @decorator def house(): print('Rough house...') house()
So what is the execution order?
def decorator(func): print('decorator start ...') def wrapper(): print('wrapper start ...') func() print('wrapper end ...') print('decorator end ...') return wrapper @decorator def func(): print('Function execution...') # func()
Execution results:
decorator start ...
decorator end ...
Execution sequence: load decorator - > Load original func - > execute decorator - > func. Now point to wrapper
- When executing the decorator, pass the original func as an argument to the formal parameter of the decorator
2.1 with reference
Or take the house decorated above as an example. We are from the decoration team and need to know the house address.
def decorator(func): def wrapper(address): func(address) print('Painting') print('Lay the floor') print('Buy furniture') print('Hardbound repair room, you can check in~~~') return wrapper @decorator def house(address): print('The house is: {}It's a blank room...'.format(address)) house('Hangzhou West Lake')
With the business expansion, we should not only decorate the house, but also decorate the factory. When decorating the factory, we also need to know the area of the plant. The process of decorating a house is similar to that of decorating a factory, but the received parameters are different. Can we use the same decorator?
def decorator(func): def wrapper(*args): func(*args) print('Painting') print('Lay the floor') print('Buy furniture') print('Hardbound repair room, you can check in~~~') return wrapper @decorator def house(address): print('The house is: {} It's a blank room...'.format(address)) @decorator def factory(address, area): print('Factory in: {} It is a blank house with a construction area of: {}'.format(address, area)) house('Hangzhou West Lake') factory('Hangzhou West Lake', 100)
Similarly, if the parameter has a default value parameter, the formal parameter needs to add * * kwargs:
def decorator(f): pass def wrapper(*args, **kwargs): pass f(*args, **kwargs) pass pass return wrapper
2.2. With return value
Continue to take the decoration of the house as an example. Now we need to make a budget for the decoration of the house to see how much it costs for blank house + decoration.
def decorator(f): def wrapper(*args, **kwargs): ret = f(*args, **kwargs) print('Painting') print('Lay the floor') print('Buy furniture') print('Hardbound repair room, you can check in~~~') ret += 10000 return ret return wrapper @decorator def house(address, area): print('The house is: {}It's a rough house, the measure of area:{}'.format(address, area)) return 50000 cost = house('Hangzhou West Lake', 100) print('Estimated cost:{}'.format(cost))
2.3 decorator with reference
Simple example:
def decrator(*dargs, **dkargs): def wrapper(func): def _wrapper(*args, **kargs): print("Decorator parameters:", dargs, dkargs) print("Function parameters:", args, kargs) return func(*args, **kargs) return _wrapper return wrapper @decrator(1, 2, a=1, b=2) def f(): print('Function execution') f()
Execution sequence: load decorator - > Load F - > execute decorator - > return wrapper - > execute wrapper - > put back_ wrapper,f points to_ wrapper
- analysis
- The returned wrapper is executed directly
General decorator parameters are rarely used.
2.4. Multiple decorators
def decorator_1(f): print('decorator_1 start') def wrapper(*args, **kwargs): print('wrapper_1 start') ret = f(*args, **kwargs) print('wrapper_1 end') return ret print('decorator_1 end') return wrapper def decorator_2(f): print('decorator_2 start') def wrapper(*args, **kwargs): print('wrapper_2 start') ret = f(*args, **kwargs) print('wrapper_2 end') return ret print('decorator_2 end') return wrapper @decorator_2 @decorator_1 def hello(): print('hello python') hello()
It can be seen that when multiple decorators decorate the same function, it will be a nested decoration result, that is, first execute a decorator close to the function, and then decorate the execution result with a decorator far from the function.
2.4 summary
Decorator function:
- Import log
- Statistics execution time
- Preprocessing before executing a function
- Cleanup function after function execution
- Permission verification and other scenarios
- cache
Generic function decorator format:
def decorator(f): pass def wrapper(*args, **args): pass ret = f(*args, **args) pass return ret pass return wrapper