学习笔记-装饰器
一. 知识铺垫
函数嵌套:
1 def hi(name="yasoob"): 2 def greet(): 3 return "now you are in the greet() function" 4 5 def welcome(): 6 return "now you are in the welcome() function" 7 8 if name == "yasoob": 9 return greet 10 else: 11 return welcome 12 13 a = hi() 14 print(a) 15 #outputs: <function greet at 0x7f2143c01500> 16 17 #上面清晰地展示了`a`现在指向到hi()函数中的greet()函数 18 #现在试试这个 19 20 print(a()) 21 #outputs: now you are in the greet() function
函数作为参数:
1 def hi(): 2 return "hi yasoob!" 3 4 def doBeforeHi(func): 5 print("I am doing some boring work before executing hi()") 6 print(func()) 7 8 doBeforeHi(hi) 9 #outputs:I am doing some boring work before executing hi() 10 # hi yasoob!
二. 装饰器
1.概念
装饰器是修改函数功能的函数
1 def a_new_decorator(a_func): 2 3 def wrapTheFunction(): 4 print("I am doing some boring work before executing a_func()") 5 6 a_func() 7 8 print("I am doing some boring work after executing a_func()") 9 10 return wrapTheFunction 11 12 def a_function_requiring_decoration(): 13 print("I am the function which needs some decoration to remove my foul smell") 14 15 a_function_requiring_decoration() 16 #outputs: "I am the function which needs some decoration to remove my foul smell" 17 18 a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) 19 #now a_function_requiring_decoration is wrapped by wrapTheFunction() 20 21 a_function_requiring_decoration() 22 #outputs:I am doing some boring work before executing a_func() 23 # I am the function which needs some decoration to remove my foul smell 24 # I am doing some boring work after executing a_func()
使用@符号生成被装饰函数
1 @a_new_decorator 2 def a_function_requiring_decoration(): 3 print("I am the function which needs some decoration to remove my foul smell") 4 5 a_function_requiring_decoration() 6 #outputs: I am doing some boring work before executing a_func() 7 # I am the function which needs some decoration to remove my foul smell 8 # I am doing some boring work after executing a_func() 9 10 #the @a_new_decorator is just a short way of saying: 11 a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
问题:打印被装饰函数名,会发现函数"a_function_requiring_decoration"被函数“warpTheFunction”代替了,即装饰器重写了原函数的名字和注释文档。
1 print(a_function_requiring_decoration.__name__) 2 # Output: wrapTheFunction
解决方法:使用functools.wraps函数
1 from functools import wraps 2 3 def a_new_decorator(a_func): 4 @wraps(a_func) 5 def wrapTheFunction(): 6 print("I am doing some boring work before executing a_func()") 7 a_func() 8 print("I am doing some boring work after executing a_func()") 9 return wrapTheFunction 10 11 @a_new_decorator 12 def a_function_requiring_decoration(): 13 print("I am the function which needs some decoration to remove my foul smell") 14 15 print(a_function_requiring_decoration.__name__) 16 # Output: a_function_requiring_decoration
2. 带参数的装饰器
在装饰器函数上加一层“带参数的包裹函数”
1 from functools import wraps 2 3 def logit(logfile='out.log'): 4 def logging_decorator(func): 5 @wraps(func) 6 def wrapped_function(*args, **kwargs): 7 log_string = func.__name__ + " was called" 8 print(log_string) 9 # 打开logfile,并写入内容 10 with open(logfile, 'a') as opened_file: 11 # 现在将日志打到指定的logfile 12 opened_file.write(log_string + '\n') 13 return func(*args, **kwargs) 14 return wrapped_function 15 return logging_decorator 16 17 @logit() 18 def myfunc1(): 19 pass 20 21 myfunc1() 22 # Output: myfunc1 was called 23 # 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串 24 25 @logit(logfile='func2.log') 26 def myfunc2(): 27 pass 28 29 myfunc2() 30 # Output: myfunc2 was called 31 # 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串
装饰器执行顺序:logit() ——> @logging_decorator ——> wrapped_function() ——> func() 即 myfunc1()/myfunc2()
3.装饰器类
在装饰器的功能上加上类的“继承”优势,可以在子类中实现“新功能”,该“新功能”会被装饰到目标函数上。
以装饰器类的方式重构logit()
1 from functools import wraps 2 3 class logit(object): 4 def __init__(self, logfile='out.log'): 5 self.logfile = logfile 6 7 def __call__(self, func): 8 @wraps(func) 9 def wrapped_function(*args, **kwargs): 10 log_string = func.__name__ + " was called" 11 print(log_string) 12 # 打开logfile并写入 13 with open(self.logfile, 'a') as opened_file: 14 # 现在将日志打到指定的文件 15 opened_file.write(log_string + '\n') 16 # 现在,发送一个通知 17 self.notify() 18 return func(*args, **kwargs) 19 return wrapped_function 20 21 def notify(self): 22 # logit只打日志,不做别的 23 pass
给logit创建子类,添加发送Email功能
1 class email_logit(logit): 2 ''' 3 一个logit的实现版本,可以在函数调用时发送email给管理员 4 ''' 5 def __init__(self, email='admin@myproject.com', *args, **kwargs): 6 self.email = email 7 super(email_logit, self).__init__(*args, **kwargs) 8 9 def notify(self): 10 # 发送一封email到self.email 11 # 这里就不做实现了 12 pass
4.类装饰器
5.使用场景
- 授权:
1 from functools import wraps 2 3 def requires_auth(f): 4 @wraps(f) 5 def decorated(*args, **kwargs): 6 auth = request.authorization 7 if not auth or not check_auth(auth.username, auth.password): 8 authenticate() 9 return f(*args, **kwargs) 10 return decorated
- 日志:
1 from functools import wraps 2 3 def logit(func): 4 @wraps(func) 5 def with_logging(*args, **kwargs): 6 print(func.__name__ + " was called") 7 return func(*args, **kwargs) 8 return with_logging 9 10 @logit 11 def addition_func(x): 12 """Do some math.""" 13 return x + x 14 15 16 result = addition_func(4) 17 # Output: addition_func was called
- 多种参数:
参考资料: