写python中的装饰器

python中的装饰器主要用于在已有函数实现功能前附加需要输出的信息,下面将用实例展示我如何写装饰器。

 

首先分别尝试写装饰器装饰一个无参函数和一个有参函数(被装饰函数仅输出,无返回值情况下)

 

 1 def my_log(func):
 2     def wrapper():
 3         print('decorator works')
 4         func()
 5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
 6 
 7 @my_log
 8 def run(): # run=my_log(run)=wrapper 返回的是wrapper函数对象,未运行
 9     print('run')
10 
11 run() #run()=wrapper() 此时调用并运行了wrapper()函数
12 
13 # 运行结果如下
14 # decorator works
15 # run
 1 def my_log(func):
 2     def wrapper():
 3         print('decorator works')
 4         func()
 5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
 6 
 7 @my_log
 8 def add(x,y):   #add=my_log(add)=wrapper 返回的也是wrapper函数对象,未运行
 9     print(x+y)
10 
11 add(3,6)    
12 #试图调用并运行wrapper()
13 
14 #结果出错
15 #TypeError: wrapper() takes 0 positional arguments but 2 were given
16 # 原因是运行add(3,6)时传入了两个参数add(3,6)=wrapper(3,6) 但wrapper()函数中并无参数接收

 

修改后的正确装饰器函数为(被装饰函数仅输出,无返回值情况下)

 1 def my_log(func):
 2     def wrapper(x,y):
 3         print('decorator works')
 4         func(x,y)
 5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
 6 
 7 @my_log
 8 def add(x,y):   #add=my_log(add)=wrapper 返回的也是wrapper函数对象,未运行
 9     print(x+y)
10 
11 add(3,6)
12 
13 # 运行结果如下
14 # decorator works
15 # 9

 

但并不是完善的装饰器函数(被装饰函数仅输出,无返回值情况下)

 1 def my_log(func):
 2     def wrapper(x,y):
 3         print('decorator works')
 4         func(x,y)
 5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
 6 
 7 @my_log
 8 def add(x,y):
 9     print(x+y)
10 
11 add(3,6)
12 
13 @my_log
14 def run():
15     print('run')
16 
17 run()
18 
19 # 但添加无参函数run()时又出现了错误
20 # TypeError: wrapper() missing 2 required positional arguments: 'x' and 'y'
21 # 因run()=wrapper()未传给wrapper所必需的两个参数

 

此时需要修改装饰器中wrapper函数为非关键字可变参数和关键字可变参数来保证 无论是否有参数传入都正常运行(被装饰函数仅输出,无返回值情况下)

 1 def my_log(func):
 2     def wrapper(*args,**kwargs):
 3         print('decorator works')
 4         func(*args,**kwargs)
 5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
 6 
 7 @my_log
 8 def add(x,y):
 9     print(x+y)
10 
11 add(3,6)
12 
13 @my_log
14 def run():
15     print('run')
16 
17 run()
18 
19 # 运行结果如下
20 # decorator works
21 # 9
22 # decorator works
23 # run

 

但此时装饰器函数并未完全正确(被装饰函数仅输出,无返回值情况下)

 1 def my_log(func):
 2     def wrapper(*args,**kwargs):
 3         print('decorator works')
 4         func(*args,**kwargs)
 5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
 6 
 7 @my_log
 8 def add(x,y):
 9     print(x+y)
10 
11 @my_log
12 def run():
13     print('run')
14 
15 print(run.__name__)
16 print(add.__name__)
17 
18 # 运行结果如下
19 # wrapper
20 # wrapper
21 #因run=my_log(run)=wrapper 偷偷修改了run方法和add方法的__name__属性

 

需要在装饰器函数内部添加@wraps来保证被装饰函数__name__属性不被修改(被装饰函数仅输出,无返回值情况下)

 1 from functools import wraps
 2 
 3 def my_log(func):
 4     @wraps(func)
 5     def wrapper(*args,**kwargs):
 6         print('decorator works')
 7         func(*args,**kwargs)
 8     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
 9 
10 @my_log
11 def run(): # run=my_log(run)=wrapper 返回的是wrapper函数对象,未运行
12     print('running')
13 
14 run()
15 # run()=wrapper()
16 
17 print("函数run的__name__是%s"%run.__name__)
18 print('-'*30)
19 
20 @my_log # add=my_log(add)=wrapper 返回的也是wrapper函数对象
21 def add(a,b):
22     print(a+b)
23 
24 add(3,4)
25 # add(3,4)=wrapper(3,4)
26 print("函数add的__name__是%s"%add.__name__)
27 
28 # 运行结果如下
29 # running
30 # 函数run的__name__是run
31 # ------------------------------
32 # decorator works
33 # 7
34 # 函数add的__name__是add

 

被装饰函数有返回值时

 

 1 from functools import wraps
 2 
 3 
 4 #***************错误*************************
 5 def login_required(func):
 6     @wraps(func)
 7     def wrapper(*args,**kwargs):
 8         print('hello world')
 9         func(*args,**kwargs)
10     return wrapper
11 
12 @login_required
13 def index():
14     return 'aaaaaaaaaaa'
15 
16 a=index()
17 print(a)
18 #index=login_required()=wrapper
19 #index()=wrapper()
20 # def wrapper(*args,**kwargs):
21 #     print('hello world')
22 #     index()   此处只是调用index函数得到'aaaaaaaaaaa'   但wrapper函数并未返回该值
23 
24 # hello world
25 # None      可见wrapper()调用index()函数并未返回'aaaaaaaaaaa'
26 
27 #**************正确********************
28 def login_required2(func):
29     @wraps(func)
30     def wrapper(*args,**kwargs):
31         print('hello world')
32         return func(*args,**kwargs)
33     return wrapper
34 
35 @login_required2
36 def index():
37     return 'aaaaaaaaaaa'
38 
39 print('-'*20)
40 a=index()
41 print(a)
42 
43 
44 
45 #输出结果如下
46 #--------------------
47 # hello world
48 # aaaaaaaaaaa

 

 

 

 

 

最后总结:

1.装饰器中定义的函数要使用*args 和**kwargs 组合来接收任何可能被装饰函数的参数,在装饰器中的wrapper函数中执行原函数时需传入*args 和**kwargs

2.需使用functools.wraps在装饰器中wrapper函数前将wrapper函数用@wraps包裹,防止被装饰函数__name__属性被修改

 

以下来自 CS实验室 大佬喵的 https://mp.weixin.qq.com/s?timestamp=1531273713&src=3&ver=1&signature=S-vLsC7yG6GltzbaRovEtemNFSFg3Ps*AQnH1hc3E7-huMUuZbG-i3m0c-8pkEihTO1UIA9wF4Ze8tlMKitOtFb8-eDjsQhYq7KDFDtPZpcotQikbQg8DRhzdgQHArojOFIjBhPb0wAnLzZ2hGn5PXYI4HjRy5CiEpNwr1ii09Y=

 

 1 def red_oil(func):
 2     print("ready to paint!")
 3 
 4     def red_wall_func():
 5         print("red wall!")
 6 
 7     return red_wall_func
 8 
 9 
10 @red_oil
11 def wall():
12     print("wall!")
13 
14 if __name__=="__main__":
15     print("start painting!!!")

执行结果如下:

 1 ready to paint!

2 start painting!!! 

 

 1 def red_oil(func):
 2     print("ready to paint!")
 3 
 4     def red_wall_func():
 5         print("red wall!")
 6 
 7     return red_wall_func
 8 
 9 
10 @red_oil
11 def wall():
12     print("wall!")
13 
14 if __name__=="__main__":
15     print("start painting!!!")
16     wall()

执行结果如下:
 1 ready to paint!

2 start painting!!!

3 red wall! 

 

装饰器在被装饰的函数被定义时立即执行,被装饰的函数在运行时才执行。

posted @ 2018-04-16 20:38  xajh00789  阅读(435)  评论(0编辑  收藏  举报