Learn the way of decorators

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 a

 A function whose two arguments are parameters.

Example:  z = f(x, y) converts to the form z = f(x)(y)

Example: Curry the addition function
def 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
    9
Requirement 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
65
A. 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
15
B. 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
85
C. 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.
11
Third, 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

None
    B. 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.

Fourth, the copy_properties of the decorator:
    # 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 add
6. 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
11
7. 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 0	
Eight, functools module:

    A. functools module 1 

functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES)

    Similar to copy_properties function.
    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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325505300&siteId=291194637