装饰器(Decorators)是 Python 的一个重要部分。简单地说:装饰器是修改其他函数的功能的函数。
一切皆对象 首先我们来理解下 Python 中的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def hi (name="Python" ): return "hi " + name print (hi()) greet = hi print (greet()) del hiprint (hi()) print (greet())
在函数中定义函数 在函数中定义另外的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def hi (name="Python" ): print ("start hi() function" ) def greet (): return "greet() function" def welcome (): return "welcome() function" print (greet()) print (welcome()) print ("end hi() function" ) hi() greet()
从函数中返回函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def hi (name="Python" ): def greet (): return "greet() function" def welcome (): return "welcome() function" if name == "Python" : return greet else : return welcome a = hi() print (a) print (a())print (hi('we' )())
将函数作为参数传给另一个函数 1 2 3 4 5 6 7 8 9 10 def hi (): return "hi Python!" def doSomethingBeforeHi (func ): print ("before executing hi()" ) print (func()) doSomethingBeforeHi(hi)
装饰器 @
作用:将函数传入装饰器函数。
不使用 @
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def a_new_decorator (a_func ): def wrapTheFunction (): print ("before executing a_func()" ) a_func() print ("after executing a_func()" ) return wrapTheFunction def a_function_requiring_decoration (): print ("executing a_function_requiring_decoration()" ) a_function_requiring_decoration() a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) a_function_requiring_decoration()
使用 @
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def a_new_decorator (a_func ): def wrapTheFunction (): print ("before executing a_func()" ) a_func() print ("after executing a_func()" ) return wrapTheFunction @a_new_decorator def a_function_requiring_decoration (): print ("executing a_function_requiring_decoration()" ) a_function_requiring_decoration()
通过代码可以发现,@a_new_decorator
的作用相当于:
1 a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
解决装饰器重写问题 1 2 print (a_function_requiring_decoration.__name__)
原因就是装饰器重写了我们函数的名字和注释文档(docstring)。
解决方法:引入 functools.wraps
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from functools import wrapsdef a_new_decorator (a_func ): @wraps(a_func ) def wrapTheFunction (): print ("before executing a_func()" ) a_func() print ("after executing a_func()" ) return wrapTheFunction @a_new_decorator def a_function_requiring_decoration (): print ("executing a_function_requiring_decoration()" ) print (a_function_requiring_decoration.__name__)
@wraps
接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这样可以在装饰器里面访问在装饰之前的函数的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from functools import wrapsdef decorator_name (f ): @wraps(f ) def decorated (*args, **kwargs ): if not can_run: return "Function will not run" return f(*args, **kwargs) return decorated @decorator_name def func (): return ("Function is running" ) can_run = True print (func()) can_run = False print (func())
带参数的装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from functools import wraps def logit (logfile='out.log' ): def logging_decorator (func ): @wraps(func ) def wrapped_function (*args, **kwargs ): log_string = func.__name__ + " was called" print (log_string) with open (logfile, 'a' ) as opened_file: opened_file.write(log_string + '\n' ) return func(*args, **kwargs) return wrapped_function return logging_decorator @logit() def myfunc1 (): pass myfunc1() @logit(logfile='func2.log' ) def myfunc2 (): pass myfunc2()
装饰器类 使用类装饰器主要依靠类的 __call__
方法,当使用 @
形式将装饰器附加到函数上时,就会调用此方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from functools import wraps class logit (object ): def __init__ (self, logfile='out.log' ): self.logfile = logfile def __call__ (self, func ): @wraps(func ) def wrapped_function (*args, **kwargs ): log_string = func.__name__ + " was called" print (log_string) with open (self.logfile, 'a' ) as opened_file: opened_file.write(log_string + '\n' ) self.notify() return func(*args, **kwargs) return wrapped_function def notify (self ): pass @logit() def myfunc1 (): pass myfunc1()
给 logit
创建子类,模拟添加 email
的功能,@email_logit
将会和 @logit
产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from python1 import logitclass email_logit (logit ): ''' 一个logit的实现版本,可以在函数调用时发送email给管理员 ''' def __init__ (self, email='admin@myproject.com' , *args, **kwargs ): self.email = email super (email_logit, self).__init__(*args, **kwargs) def notify (self ): pass
装饰器顺序 一个函数还可以同时定义多个装饰器,比如:
1 2 3 4 5 @a @b @c def f (): pass
它的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于
装饰器使用场景 授权(Authorization) 1 2 3 4 5 6 7 8 9 10 from functools import wraps def requires_auth (f ): @wraps(f ) def decorated (*args, **kwargs ): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): authenticate() return f(*args, **kwargs) return decorated
日志(Logging) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from functools import wraps def logit (func ): @wraps(func ) def with_logging (*args, **kwargs ): print (func.__name__ + " was called" ) return func(*args, **kwargs) return with_logging @logit def addition_func (x ): """Do some math.""" return x + x result = addition_func(4 )
参考:
Python 函数装饰器