学习笔记-装饰器

一. 知识铺垫

函数嵌套:

 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
View Code

函数作为参数:

 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!
View Code

 二. 装饰器

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()
View Code

   使用@符号生成被装饰函数

 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)
View Code

  问题:打印被装饰函数名,会发现函数"a_function_requiring_decoration"被函数“warpTheFunction”代替了,即装饰器重写了原函数的名字和注释文档。

1 print(a_function_requiring_decoration.__name__)
2 # Output: wrapTheFunction
View Code

  解决方法:使用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
View Code

 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 的文件出现了,里面的内容就是上面的字符串
View Code

  装饰器执行顺序: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
View Code

  给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
View Code

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
View Code
  • 日志:
 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
View Code
  • 多种参数:

 


 

参考资料:

  https://www.runoob.com/w3cnote/python-func-decorators.html

posted @ 2021-11-02 16:31  getright  阅读(18)  评论(0编辑  收藏  举报