Learn the way of decorators
1. Currying
A. [Currying 1] of currying currying:
Refers to the process of changing the original function that accepts two parameters into a new function that accepts one parameter. The new function returns aA function whose two arguments are parameters.
Example: z = f(x, y) converts to the form z = f(x)(y)
Example: Curry the addition functiondef add(x, y):
return x + y
B, Curry the currying [Curry 2]:
Example: Curry the addition function
def add(x, y):
return x + y
is converted as follows:
def add(x):
def _add(y):
return x+y
return _add
add(5)(6)
Convert functions to curried functions by nesting functions
2. Decorator
Requirement 1: An addition function, want to enhance its function, can output called and called parameter information.def add(x,y):
return x+y
Add information output function: def add(x,y): print('call add,x+y') # The log is output to the console. The addition function above return x+y fulfills the requirements, but has the following disadvantages: a. The coupling of print statements is too high b. The addition function is a business function, and the function of outputting information is a non-business function code, which should not be Put it in the business addition function.
Requirement 2: Do a good job of business functions, but it is a problem to pass parameters to the fn function call.
code show as below:
def add(x,y): return x+y def logger(fn): print('begin') #Enhanced output x = fn(4,5) print('end') # Enhanced functionality. return x print(logger(add))Results of the:
begin end 9Requirement 3: Solve the problem of parameter transmission and make further changes.
code show as below:
def add(x,y): # print(y) return x+y def logger(fn,*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x print(logger(add,5,y=60))Results of the:
begin #60 end 65A. Simplification of currying:
code show as below:
def add(x,y): return x+y def logger(fn): def wrapper(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x return wrapper #------------------------# # add = logger(add) # print(add(x=5,y=10)) #------------------------# #Replace the above two lines with the following line of code, which is currying: print(logger(add)(5,y=10))Results of the:
begin end 15B. Decorator syntax sugar:
code show as below:
def logger(fn): def wrapper(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x return wrapper @logger #The previous @logger is equivalent to add = logger(add) def add(x,y): return x+y print(add(40,45))
# What is @logger? Answer: Equivalent to add = logger(add) This is the decorator syntax.
Results of the:
begin end 85C. Decorator (no parameters):
A decorator is a non-intrusive code
that is a function.
The function works on its formal parameters.
The return value is also a function.
You can use the @functionname method to simplify the call.
Decorators and higher-order functions
Decorators are higher-order functions, but a decorator is a decoration of the functionality passed into the function. (feature enhancement)
[Decorator example 1]:
code show as below:
import datetime,time def logger(fn): def wrap(*args,**kwargs): #before feature enhancements. print('args={},kwargs={}'.format(args,kwargs)) start = datetime.datetime.now() ret = fn (* args, ** kwargs) #after feature enhancements. duration = datetime.datetime.now() -start print("function {} took{}s.".format(fn.__name__,duration.total_seconds())) return right return wrap @logger # @logger is equivalent to add = logger(add) def add(x,y): print('=======call add=========') time.sleep(2) return x+y print(add(4,y=7))Results of the:
args=(4,),kwargs={'y': 7} =======call add========= function addtook2.000115s. 11Third, the documentation string
A, the documentation string:
Python is the documentation string Documentation Strings
in the first line of the function statement block, and is used to multi-line text, so use triple quotation marks.
The convention is to capitalize the first letter, with an overview on the first line, a blank line, and a detailed description on the third line.
This documentation can be accessed using the special attribute __doc__
code show as below:
def add(x,y): """this is a function of addition""" a= x+y return x+y print("name ={}\ndoc={}".format(add.__name__,add.__doc__)) print(help(add))Results of the:
name =add doc=this is a function of addition Help on function add in module __main__: add(x, y) this is a function of addition NoneB. docstring side effects:
code show as below:
def logger(fn): def wrapper(*args,**kwargs): 'I am wrapper' print('begin') x = fn(*args,**kwargs) print('end') return x return wrapper @logger # @logger is equivalent to: add = logger(add) def add(x,y): """this is a function for add""" return x+y print("name={},doc={}".format(add.__name__,add.__doc__))Results of the:
name=wrapper,doc=I am wrapper
The original function attributes in the above code have been replaced, but the printed result is no longer the original attribute, and using the decorator, our requirement is to view the attributes of the encapsulated function and how to solve it.
# Provides a function, the packaged function property ===copy==="wrapper function property.
code show as below:
def copy_properties(src,dst): # Can be transformed into a decorator. dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ def logger(fn): def wrapper(*args,**kwargs): 'I am wrapper' print('begin') x = fn(*args,**kwargs) print('end') return x copy_properties(fn,wrapper) return wrapper @logger # add = logger(add) def add(x,y): """ this is function for add""" return x+y print("name={},doc={}".format(add.__name__,add.__doc__))Results of the:
name=add,doc= this is function for add #Realize to view the properties of the corresponding function.
5. Transition: [Change the copy_properties attribute of the decorator to a decorator with parameters]: The properties of the wrapped function will be overwritten by the copy_properties function to overwrite the wrapper function.
All decorated functions need to copy these properties, this function is very general.
The copied function can be constructed as a decorator function, with a parameter decorator.
A. Decorator with parameters: Provide a function, which is transformed into a decorator with parameters.
code show as below:
def copy_properties(src): def _copy(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ return dst return _copy def logger(fn): @copy_properties(fn) #wrapper = wrapper(fn)(wrapper) def wrapper(*args,**kwargs): "I am wrapper" print('begin') x = fn(*args,**kwargs) print('end') return x return wrapper @logger #add = logger(add) def add(x,y): """this is a function for add""" return x+y print("name={},doc={}".format(add.__name__,add.__doc__))
Results of the
name=add,doc=this is a function for add6. Decorator with parameters:
Requirements: Get the execution time of the function, and record the function whose duration exceeds the threshold.
import datetime,time def logger(duration): def _logger(fn): @copy_properties(fn) # wrapper = wrapper(fn)(wrapper) def wrapper(*args,**kwargs): start = datetime.datetime.now() ret = fn (* args, ** kwargs) delta = (datetime.datetime.now() - start).total_seconds() print('so slow') if delta > duration else print('so fast') return right return wrapper return _logger @logger(5) #add = logger(5)(add) def add(x,y): time.sleep(3) return x+y print(add(5,6))Results of the:
so fast 117. Decorator with parameters:
It is a function
function as its formal parameter. The
return value is a decorator function
without parameters. Calling it in the way of @functionname (parameter list)
can be seen as adding another layer to the outer layer of the decorator. Function
A, Decorator 1 with parameters:
Extract the recorded functions, so that the output can be flexibly controlled by externally provided functions
code show as below:
import datetime,time,functools def copy_properties (sti): def _copy(pte): pte.__name__ = sti.__name__ pte .__ doc__ = sti .__ doc__ return pte #Be sure to return the return value, or not to return a None value, or the results of the following function calls are all None values, which is meaningless. return _copy #Be sure to return the return value, or whether to return a None value, or the results of the following function calls are all None values, which is meaningless. def logger(duration,func=lambda name,duration:print('{}took {}s'.format(name,duration))): def _logger(fn): @copy_properties(fn) #相当于:wrapper =wrapper copy_properties(fn)(wrapper) # Here it evolves from the following: @_copy wrapper_copy(wrapper) def wrapper(*args,**kwargs): start =datetime.datetime.now() ret = fn (* args, ** kwargs) delta = (datetime.datetime.now()-start).total_seconds() if delta > duration: func(fn.__name__,duration) return right return wrapper return _logger @logger(2) #add= logger(add) def add(x,y): 'i am add' return x+y print(add(4,5),add.__name__,add.__name__,add.__doc__) print(logger(add))Results of the:
9 add add i am add <function logger.<locals>._logger at 0x00000000023748C8> Process finished with exit code 0Eight, functools module:
A. functools module 1
functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES)
Wrapper wrapper function, updated, wrapped wrapped function, data source.
In the tuple WRAPPER_ASSIGNMENTS are the attributes to be overridden.
'_module_', '_name_', '_qualname_', '_doc_', '_annotations_'
tuple WRAPPER_UPDATES is the attribute to be updated, _dict_ attribute dictionary.
Add a _wrapped_ attribute, keep the wrapped attribute
functools module 1 Example: functools.update_wrapper(wrapper,fn)#wrapped function wrapped and wrapped function fn
code show as below:
import datetime,time,functools def logger(duration,func=lambda name,duration:print('{} took {}s.'.format(name,duration))): def _logger(fn): def wrapper(*args,**kwargs): start = datetime.datetime.now() ret = fn (* args, ** kwargs) delta = (datetime.datetime.now()-start).total_seconds() if delta > duration: func(fn.__name__,duration) return right return functools.update_wrapper(wrapper,fn) #wrapper function wrapper and wrapped function fn return _logger @logger(5) #add = logger(5)(add) def add(x,y): time.sleep(1) return x+y print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,sep='\n')Results of the:
11 add <function add at 0x0000000004E4A9D8> {'__wrapped__': <function add at 0x0000000004E4A9D8>B. functools module 2:
@functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES) is
similar to the copy_properties function
wrapped. The wrapped function
tuple WRAPPER_ASSIGNMENTS is the attribute to be overwritten
'_module_','_name_','_qualname_', '_doc_', '_annotations_'
module name, name, qualified name, documentation, parameter annotations.
The tuple WARAPPER_UPDATES is the attribute to be updated, the _dict_ attribute dictionary
adds a _wrapped_ attribute, and retains the wrapped function functools module 2 Example 1: @functools.wraps(fn)
code show as below:
import datetime,time,functools def logger(duration,func=lambda name,duration:print('{} took {}s'.format(name,duration))): def _logger(fn): @functools.wraps(fn) def wrapper(*args,**kwargs): start = datetime.datetime.now() ret = fn (* args, ** kwargs) delta = (datetime.datetime.now() -start).total_seconds() if delta > duration: func(fn.__name__,duration) return right return wrapper return _logger @logger(5) #equivalent to add = logger(5)(add) def add(x,y): time.sleep(1) return x+y print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,sep= '\n')Results of the:
11 add <function add at 0x0000000004E4AAE8> {'__wrapped__': <function add at 0x0000000004E4AAE8>}C. Example 2 of functools module 2::@functools.wraps(fn) Example 2 of functools module 2:
code show as below:
import functools,datetime,time # def copy_perporties(tif): # def _copy(fit): # fit.__name__ = tif.__name__ # fit.__doc__ = tif.__doc__ # return fit # return _copy def logger(duration=5,func=lambda name,duration:print(name,duration)): def _logger(fn): #@copy_properties(fn) #The first 6 lines and this line are basically the same as the implementation of the curried decorator call below. @functools.wraps(fn) #followed by the wrapped function fn def wrapper(*args,**kwargs): 'i am wrapper' print('args={} kwargs={}'.format(args,kwargs)) start = datetime.datetime.now() ret = fn (* args, ** kwargs) print(id(fn),'1___________') delta = (datetime.datetime.now()-start).total_seconds() if delta > duration: func(fn.__name__,duration) print("funcation {} took {} s".format(fn.__name__,duration)) return right #copy_perproties(fn,wrapper) #functools.update_wrapper(wrapper,fn) return wrapper return _logger @logger() #相当于:add= loger(add) #logger(5)-->_logger add = _logger(add) def add(x,y): 'this is a add funcation' print('====call add======') time.sleep(2) return x+y print(add(4,y=7),add.__name__,add.__doc__,add.__wrapped__,sep=’\n’) print(id(add.__wrapped__),'2____________')Results of the:
args=(4,) kwargs={'y': 7} ====call add====== 42548352 1___________ funcation add took 5 s 11 add this is a add funcation <function add at 0x0000000002893C80> 42548352 2____________ Process finished with exit code 0