Python基础(十四)—装饰器 wrapper

in python基础 with 0 comment

装饰器 wrapper

先上一篇博文:详解Python装饰器

def debug(func):
    def wrapper():
        print('[DEBUG]: enter {}()'.format(func.__name__))
        return func()
    return wrapper

@debug
def say_hello():
    print('hello!')
say_hello()
"""
[DEBUG]: enter say_hello()
hello!
"""

将 @debug 放在say_hello() 前面,相当于执行了
say_hello = debug(say_hello)
由于debug()是一个装饰器,返回了wrapper函数,所以原来的say_hello()依然存在,只是现在同名的now变量指向了新的函数,于是调用say_hello()将执行新的函数,即在debug()函数中返回的wrapper()函数。

def debug(func):
    def wrapper(*args, **kwargs):
        print('[DEBUG]: enter {}()'.format(func.__name__))
        return func(*args, **kwargs)
    return wrapper

@debug
def say_hello(name, something):
    print('hello!{}{}'.format(name, something))

say_hello('luozheng', '.')
"""
[DEBUG]: enter say_hello()
hello!luozheng.
"""

Python提供了可变参数args和关键字参数*kwargs,有了这两个参数,装饰器就可以用于任意目标函数。

高阶装饰器

def logging(level):
    def wrapper(func):
        def inner_wrapper(*args, **kwargs):
            print('[{level}]: enter function {func}()'.format(
                level=level,
                func=func.__name__))
            return func(*args, **kwargs)
        return inner_wrapper
    return wrapper

@logging(level='INFO')
def say(something):
    print('say {}!'.format(something))

# 如果没有使用@语法,等同于
# say = logging(level='INFO')(say)
@logging(level='DEBUG')
def do(something):
    print('do {}...'.format(something))

if __name__ == '__main__':
    say('hello')
    do('my work')

"""
[INFO]: enter function say()
say hello!
[DEBUG]: enter function do()
do my work...
"""

当带参数的装饰器被打在某个函数上时,比如@logging(level='DEBUG'),它其实是一个函数,会马上被执行,只要这个它返回的结果是一个装饰器时,那就没问题。细细再体会一下。

class Test():
    def __call__(self):
        print 'call me!'

t = Test()
t()  # call me

装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文)。那么用类来实现也是也可以的。我们可以让类的构造函数__init__()接受一个函数,然后重载__call__()并返回一个函数,也可以达到装饰器函数的效果。

class logging(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('[DEBUG]: enter function {func}()'.format(
            func=self.func.__name__))
        return self.func(*args, **kwargs)
@logging
def say(something):
    print('say {}'.format(something))

say('love you.')
"""
[DEBUG]: enter function say()
say love you.
"""
class logging(object):
    def __init__(self, level='INFO'):
        self.level = level

    def __call__(self, func):  # 接受函数
        def wrapper(*args, **kwargs):
            print('[{level}]: enter function {func}()'.format(
                level=self.level,
                func=func.__name__))
            func(*args, **kwargs)

        return wrapper  # 返回函数

@logging(level='INFO')
def say(something):
    print('say {}'.format(something))

say('love you.')
print(say.__name__)  # wrapper

装饰器的注意事项

def html_tags(tag_name):
    print('begin outer function.')
    def wrapper_(func):
        print('begin of inner wrapper function.')
        def wrapper(*args, **kwargs):
            content = func(*args, **kwargs)
            print('<{tag}>{content}</{tag}>'.format(tag=tag_name, content=content))
        print('end of inner wrapper function.')
        return wrapper
    print('end of outer function')
    return wrapper_

@html_tags('b')
def hello(name='Toby'):
    return 'Hello {}!'.format(name)

hello()
hello()
"""
begin outer function.
end of outer function
begin of inner wrapper function.
end of inner wrapper function.
<b>Hello Toby!</b>
<b>Hello Toby!</b>
"""
from functools import wraps
import datetime

def logging(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """print log before a function."""
        print('[DEBUG] {}: enter {}()'.format(datetime.now(), func.__name__))
        return func(*args, **kwargs)
    return wrapper

@logging
def say(something):
    """say something"""
    print('say {}!'.format(something))

print(say.__name__)  # say
print(say.__doc__) # say something
def one(func):
    print('----1----')
    def two():
        print('----2----')
        func()
    return two

def a(func):
    print('----a----')
    def b():
        print('----b----')
        func()
    return b

@one
@a
def demo():
    print('----3----')

demo()
"""
----a----
----1----
----2----
----b----
----3----
"""
Responses