python closures and decorators
1, What is a closure?
1. Scope of function
First of all, let's understand the scope of a variable in python. python has its namespace for each variable. A function can only call the global global variable or its own local variable. With this in mind, what is a closure.
2. What is a closure?
Simply put, a closure is a function dynamically generated and returned by other functions. Its key property is that the returned function can access variables in the local namespace of its creator (the creator here generally refers to the outermost function).
Generally speaking, a closure is a function that retains the binding of free variables when defining a function. In this way, when calling a function, the closure can still retain those bindings although the definition scope is unavailable. The difference between a closure and a normal function is that a closure can continue to access the local namespace of its creator even after the creator has executed it.
3. Function of closure
Closures can avoid using global values and provide some form of data hiding. It can also provide object-oriented solutions to problems. Closures can provide an alternative and more elegant solution when there are few methods (in most cases, a method) in a class. However, when the number of properties and methods becomes larger, it is better to implement a class.
4. Take two examples of closures
# Closures: functions that return functions # The function of closure is that the accessed function can access variables in the local namespace of its creator def make_closure(a): def closure(b): print('I know the variable a of outer function is %d'%a) print('The answer of plus is', a + b) return closure closure = make_closure(10) closure(3)
I know the variable a of outer function is 10 The answer of plus is 13
In this example, our closure can accept a variable of the external function and print it. The internal function does not perform any function for the time being. When the internal function is executed separately, it still retains the binding of the free variable a of the external function, which is a simple closure.
# Find any power of any number def make_multiple_of(n): def multiple(x): print('The times is {}'.format(n)) return x ** n return multiple time3 = make_multiple_of(3) time5 = make_multiple_of(5) print(time3(2)) print(time5(2))
The times is 3 8 The times is 5 32
In this example, the multiple function accepts make_ multiple_ The power specified by the of function remains make when the multiple function is executed alone_ multiple_ Of, which completes the function of finding the power.
5. Closed__ closure__ attribute
Closures have one more closure attribute than ordinary functions, which records the address of free variables.
print(time3.__closure__)
(<cell at 0x0000028A02079D00: int object at 0x00007FFC9D241F40>,)
2, Decorator
1. Ordinary decorator
In fact, implement special methods__ call__ Any object of () is called callable. Therefore, in the most basic sense, decorators are callable and can return callable. Basically, the decorator receives a function, adds some functions, and returns.
As mentioned above, in python, everything is an object, and functions are no exception, so we can assign another variable to the function to make the two variables point to the reference of the same function.
First define a simple decorator
# A decorator def make_wrapper(func): def wrapper(): print('func has been decorated') func() return wrapper def ordinary(): print('This is an ordinary function')
In order to use our defined decorator, we can call:
ordinary = make_wrapper(ordinary)
In this way, we actually use closures to package and retain the binding of some free variables and assign the order function to ourselves. However, the order function at this time has more variable binding with the initial function, so we can call it like the previous order function.
ordinary()
func has been decorated This is an ordinary function
2. Decorator grammar sugar
We can use @ wrapper_name instead of ordinary = make_ A complex assignment process such as wrapper (order) can make the code more concise.
def make_wrapper(func): def wrapper(): print('func has been decorated') func() return wrapper @make_wrapper def ordinary(): print('This is an ordinary function') ordinary()
func has been decorated This is an ordinary function
3. Standard timing decorator template
Here is a decorator that can calculate the running time of a function, providing an idea of writing a decorator.
import time from functools import wraps def time_wrapper(func): @wraps(func) # This wrap decorator is built in python and is mainly used to copy function names, comment documents and other functions def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print("It takes {:2f} second".format(time.time() - start)) return result return wrapper @time_wrapper def factorial(n): # Calculate factorial result = 1 for i in range(1, n + 1): result = result * i return result
4. Summary
The typical behavior of decorators can be summarized as: accepting and binding some free variables through closures, replacing the decorated function with a new function. They accept the same parameters, and (usually) return the value that should be returned by the decorated function, as well as some additional operations.
functools. The wraps decorator can copy the related attributes of the decorated function from func to the decorator.
3, Decorator factory function (parameterized decorator)
Python passes the decorated function as the first parameter to the decorator function. How can the decorator accept other functions? The answer is: create a decorator factory function, pass parameters to it, return a decorator, and then apply it to the function to be decorated.
Because the decorator takes parameters, we also need to take parameters when reusing syntax sugar
Generally speaking, decorators need two layers of function nesting. Since decorator factory functions need to accept parameters, decorator factory functions need three layers of function nesting.
# A function registration decorator, which is convenient to view the registered functions registry = set() def register(active=True): def decorator(func): print('runing registry(active=%s) -> wrapper(%s)' %(active, func)) if active: registry.add(func) else: registry.discard(func) # This is a decorator def wrapper(*args, **kwargs): print('do something') result = func(*args, **kwargs) return result return wrapper return decorator @register(active=False) def func1(): print('runing func1') @register(active=True) def func2(): print('runing func2') @register(active=True) def func3(): print('runing func3')
runing registry(active=False) -> wrapper(<function func1 at 0x0000028A020AC280>) runing registry(active=True) -> wrapper(<function func2 at 0x0000028A020AC820>) runing registry(active=True) -> wrapper(<function func3 at 0x0000028A020ACDC0>)
Check which functions are registered. Func1 is not registered, so there is no func1 in the result
4, Reference
http://c.biancheng.net/view/5335.html
https://www.yiibai.com/python/closure.html
https://www.yiibai.com/python/decorator.html
<FluentPYthon>