Python decorators Profile

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/cskywit/article/details/88740687

1. The basic problem decorators to achieve

Basic Python decorators

@function_wrapper
def function():
    pass

The above code is equivalent to:


def function():
    pass

function = function_wrapper(function)

Use classes to implement the decorator

class function_wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped
    def __call__(self, *args, **kwargs):
        return self.wrapped(*args, **kwargs)

@function_wrapper
def function():
    pass
   本例中的类实例用原始函数对象初始化并记录。当调用被装饰函数时,实际上调用的是装饰器对象的_call__()方法。然后再调用原始包装函数。单独传递对装饰器的调用并不是特别有用,通常希望在调用装饰器函数之前或之后做一些工作。或者可能希望在输入参数时通过装饰器修改函数运行,要到达这个目的只需要适当地修改_call__()方法来做您想做的事情。
    通常使用__name__ and __doc__来返回函数的信息,但是当使用装饰器时,函数不再像使用函数闭包时那样工作,而是返回嵌套函数的详细信息。
​```python
def function_wrapper(wrapped):
    def _wrapper(*args, **kwargs):
        return wrapped(*args, **kwargs)
    return _wrapper 

@function_wrapper
def function():
    pass 

>>> print(function.__name__)
_wrapper

If you use classes to implement a decorator, because usually there is no instance of a class _name__ attribute, attempts to access the function name actually cause AttributeError exception.

class function_wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped
    def __call__(self, *args, **kwargs):
        return self.wrapped(*args, **kwargs) 

@function_wrapper
def function():
    pass 

>>> print(function.__name__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function_wrapper' object has no attribute '__name__'

When using function closures, the solution properties of interest is copied from the nested wrapped wrapper function to function. This function names and documentation strings are correct.

def function_wrapper(wrapped):
    def _wrapper(*args, **kwargs):
        return wrapped(*args, **kwargs)
    _wrapper.__name__ = wrapped.__name__
    _wrapper.__doc__ = wrapped.__doc__
    return _wrapper 

@function_wrapper
def function():
    pass 

>>> print(function.__name__)
function

Need to manually copy the property is very laborious, if any other special attributes you want to copy to add, you need to update these properties. For example, if the copy should __module__ properties, and added __qualname__ and __annotations__ properties in python3. To correct this purpose, Python standard library provides functools.wrap () decorator to accomplish this task.

import functools 

def function_wrapper(wrapped):
    @functools.wraps(wrapped)
    def _wrapper(*args, **kwargs):
        return wrapped(*args, **kwargs)
    return _wrapper 

@function_wrapper
def function():
    pass 

>>> print(function.__name__)
function

If implemented using class wrappers, using functools.update_wrapper () function.

import functools 

class function_wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped
        functools.update_wrapper(self, wrapped)
    def __call__(self, *args, **kwargs):
        return self.wrapped(*args, **kwargs)

2. examples to explain

Examples of this section reprinted from other blog

ATM machine simulation:

def deposit():
    print('存款中.....')
def withdrawal():
    print('取款中.....')
button = 1
if button == 1:
    deposit()
else:
    withdrawal()
 存款中.....

No, the teller to enter a password ah! ! ! So, we have to add password authentication code.

def deposit():
    print('存款中.....')
def withdrawal():
    print('取款中.....')
def check_password():
    print('密码验证中...')

button = 2
if button == 1:
    check_password()
    deposit()
else:
    check_password()
    withdrawal()
密码验证中...
取款中.....

It can be seen, although to achieve a password verification function, but the code redundancy is relatively high, but now only simulate the function of withdrawals and deposits, but also query function, transfer function, etc., then even higher degree of redundancy, and with respect to withdrawals and deposits function, the reusability is not so high, so we have to further optimize the code, the internal validation function writes withdrawal and deposit functions.

def deposit():
    print('存款中.....')
def withdrawal():
    print('取款中.....')
def check_password(func):
    print('密码验证中...')
    func()

button = 2
if button == 1:
    check_password(deposit)
else:
    check_password(withdrawal)
密码验证中...
取款中.....

Although the problem has been solved, but has changed the business logic, business logic code that is needed to re-change, if we add new features, but also need to change the code. Is there any way, you can not change the extension of the original function of the case to call the original function of the original function and functions? Of course yes, this is the famous decorator in python. Or use the above code to demonstrate.

def check_password(func):
    def inner():
        print('密码验证中....')
        func()
    return inner
def deposit():
    print('存款中.....')
def withdrawal():
    print('取款中.....')

deposit = check_password(deposit)
withdrawal = check_password(withdrawal)

button = 2
if button == 1:
    deposit()
else:
    withdrawal()
密码验证中....
取款中.....

You can see, even if the calling function and the original function of the original function have not changed, but the function function has expanded, maintainable new high, which is the power of the decorator. Decorators use of the principle of closure.

The code can be further simplified using the syntactic sugar.

def check_password(func):
    def inner():
        print('密码验证中....')
        func()
    return inner

@check_password #相当于deposit = check_password(deposit)
def deposit():
    print('存款中.....')
@check_password #相当于withdrawal = check_password(withdrawal)
def withdrawal():
    print('取款中.....')


button = 2
if button == 1:
    deposit()
else:
    withdrawal()

Let's look at the case of decorator decorated with function parameter:

def celebrator(func):
    def inner():
        print('我是新增功能')
        func()
    return inner

@celebrator
def myprint(a):
    print(a)
myprint('Python小白联盟')

Output follows: TypeError: inner () takes 0 positional arguments but 1 was given
to see to know the reasons given, inner () function more than one parameter. We must note that, because the decorator function's return value is the inner, that is now myprint is equivalent to the inner. Let's change the code below:

def celebrator(func):
    def inner(str):
        print('我是新增功能')
        func(str)
    return inner

@celebrator
def myprint(a):
    print(a)
myprint('Python小白联盟')

I was new in

Python White Alliance

In order to make more decorative decorator can function, we further optimized code.

def celebrator(func):
    def inner(*args,**kwargs):
        print('我是新增功能')
        func(*args,**kwargs)
    return inner

@celebrator
def myprint(a):
    print(a)
myprint('Python小白联盟')

It is decorated with the decorative case where the return value of the function:

def celebrator(func):
    def inner(*args,**kwargs):
        print('我是新增功能')
        func(*args,**kwargs)
    return inner

@celebrator
def myprint(a):
    return a     #带有返回值   
@celebrator
def yourprint(b):
    print(b)
ret1 = myprint('Python小白联盟')
ret2 = yourprint('666')
print(ret1,ret2)

I was new in
me is a new feature
666
None None

We can see, regardless of whether the decorated function return values, and the results have no return value, the reason is simple, because the inner () function did not return a value. In order to achieve the function returns a value after being decorated still have a return value, the return value of the function and the need for inner decorative function being consistent.

def celebrator(func):
    def inner(*args,**kwargs):
        print('我是新增功能')
        ret = func(*args,**kwargs)
        return ret
    return inner

@celebrator
def myprint(a):
    return a     #带有返回值   
@celebrator
def yourprint(b):
    print(b)
ret1 = myprint('Python小白联盟')
ret2 = yourprint('666')
print(ret1,ret2)
我是新增功能
我是新增功能
666
Python小白联盟 None

You can see, the function returns a value after being decorated still have a return value, no return value of the function after being decorated is no return value, in line with the results we want.

Double syntactic sugar:

In order to demonstrate more intuitive dual syntactic sugar, I will not use the example above. As we want to achieve at the output the top row of printed '=' line and '*':

def printequal(func):
    def inner():
        print('='*15)
        func()
    return inner

def printstar(func):
    def inner():
        print('*'*15)
        func()
    return inner

@printequal
@printstar
def myprint():
    print('python小白联盟')
myprint()
===============
***************
python小白联盟

def printequal(func):
    def inner():
        print('='*15)
        func()
    return inner

def printstar(func):
    def inner():
        print('*'*15)
        func()
    return inner

@printstar
@printequal
def myprint():
    print('python小白联盟')
myprint()
***************
===============
python小白联盟

It can be seen in a superimposed manner should be the first, the outermost layer of syntactic sugar that is executed first.

Syntactic sugar arguments

def get_celebrator(char):
    def print_style(func):
        def inner():
            print(char*15)
            func()
        return inner
    return print_style

@get_celebrator('=')
@get_celebrator('*')
@get_celebrator('-')
def myprint():
    print('python小白联盟')
myprint()
===============
***************
---------------
python小白联盟

Guess you like

Origin blog.csdn.net/cskywit/article/details/88740687