[关闭]
@yanglt7 2018-12-03T03:49:25.000000Z 字数 1412 阅读 623

Python14_装饰器

Python


由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

  1. >>> def now():
  2. ... print('2017-07-16')
  3. ...
  4. >>> f=now
  5. >>> f()
  6. 2017-07-16

函数对象有一个 __ name__ 属性,可以拿到函数的名字:

  1. >>> now.__name__
  2. 'now'
  3. >>> f.__name__
  4. 'now

在函数调用前后自动打印日志,但又不希望修改 now() 函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,decorator 就是一个返回函数的高阶函数。

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

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

观察上面的 log,因为它是一个 decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助 Python 的 @ 语法,把 decorator 置于函数的定义处:

  1. >>> @log
  2. ... def now():
  3. ... print('2017-07-16')
  4. ...

调用 now() 函数,不仅会运行 now() 函数本身,还会在运行 now() 函数前打印一行日志:

  1. >>> now()
  2. call now():
  3. 2017-07-16

把 @log 放到 now() 函数的定义处,相当于执行了语句:

  1. now = log(now)

如果 decorator 本身需要传入参数,那就需要编写一个返回 decorator 的高阶函数,写出来会更复杂。比如,要自定义 log 的文本:

  1. >>> def log(text):
  2. ... def decoractor(func):
  3. ... def wrapper(*args,**kw):
  4. ... print('%s,%s():' %(text,func.__name__))
  5. ... return func(*args,**kw)
  6. ... return wrapper
  7. ... return decoractor
  8. ...
  9. >>> @log('execute')
  10. ... def now():
  11. ... print('2017-07-16')
  12. ...
  13. >>> now()
  14. execute,now():
  15. 2017-07-16
  16. >>> now.__name__
  17. 'wrapper'
  18. >>> now.__name__
  19. 'wrapper'

一个完整的 decorator 的写法如下:

  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. import functools
  2. def log(text):
  3. def decoractor(func):
  4. @functools.wraps(func)
  5. def wrapper(*args,**kw):
  6. print('%s %s():' %(text,func.__name__))
  7. return func(*args,**kw)
  8. return wrapper
  9. return decoractor
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注