装饰器就是在函数外再套一层函数,用来通用地增强函数功能,例如打印函数的日志信息。

例如,要定义一个能打印日志的decorator,可以定义如下:

  1. def log(func):
  2. def wrapper(*args, **kw):
  3. print('call %s():' % func.__name__)
  4. return func(*args, **kw)
  5. return wrapper

使用时,只需在函数前一行加上 @log

  1. @log
  2. def now():
  3. print('2015-3-25')
  4. >>> now()
  5. call now():
  6. 2015-3-25

等价于执行

  1. now = log(now)

如果装饰器本身要传入参数

  1. def log(text):
  2. def decorator(func):
  3. def wrapper(*args, **kw):
  4. print('%s %s():' % (text, func.__name__))
  5. return func(*args, **kw)
  6. return wrapper
  7. return decorator

这个3层嵌套的decorator用法如下:

  1. @log('execute')
  2. def now():
  3. print('2015-3-25')

执行结果如下:

  1. >>> now()
  2. execute now():
  3. 2015-3-25

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

  1. >>> now = log('execute')(now)

还原原函数的属性

  1. import functools
  2. def log(func):
  3. @functools.wraps(func)
  4. def wrapper(*args, **kw):
  5. print('call %s():' % func.__name__)
  6. return func(*args, **kw)
  7. return wrapper

练习

请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:

  1. def metric(fn):
  2. def wrapper(*args, **kw):
  3. start = time.time()
  4. a=fn(*args, **kw)
  5. end = time.time()
  6. print('%s executed in %s ms' % (fn.__name__,end-start))
  7. return a
  8. return wrapper

输出为:

  1. fast executed in 0.0012867450714111328 ms
  2. slow executed in 0.12357020378112793 ms

请编写一个decorator,能在函数调用的前后打印出'begin call''end call'的日志。
再思考一下能否写出一个@log的decorator,使它既支持:

@log
def f():
    pass

又支持:

@log('execute')
def f():
    pass

代码:

import functools
def log(unknow_arg):
    if type(unknow_arg)==str:
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                print('%s %s():' % (unknow_arg, func.__name__))
                print('begin call')
                a=func(*args, **kw)
                print('end call')
                return a
            return wrapper
        return decorator
    else:
        @functools.wraps(unknow_arg)
        def wrapper(*args, **kw):
            print('begin call')
            a=unknow_arg(*args, **kw)
            print('end call')
            return a
        return wrapper

输出分别为:

begin call
end call

execute f():
begin call
end call

用 class 实现装饰器

class Logger():
    def __init__(self,func):
        self._func=func

    def __call__(self):
        print(f'{self._func.__name__} is running...')
        self._func()

@Logger
def test_fun():
    print('Test!')


>>>test_fun()
test_fun is running...
Test!