写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()=my_log(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)=my_log(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()=my_log(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()=my_log(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)=my_log(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.装饰器中定义的函数要使用*args 和**kwargs 组合来接收任何可能被装饰函数的参数,在装饰器中的wrapper函数中执行原函数时需传入*args 和**kwargs

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

猜你喜欢

转载自www.cnblogs.com/xiongxueqi/p/8858402.html
今日推荐