写python中的装饰器

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

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

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

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

 def my_log(func):
def wrapper(x,y):
print('decorator works')
func(x,y)
return wrapper #返回的只是wrapper函数对象,此时并没有运行 @my_log
def add(x,y): #add=my_log(add)=wrapper 返回的也是wrapper函数对象,未运行
print(x+y) add(3,6) # 运行结果如下
# decorator works
#

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

 def my_log(func):
def wrapper(x,y):
print('decorator works')
func(x,y)
return wrapper #返回的只是wrapper函数对象,此时并没有运行 @my_log
def add(x,y):
print(x+y) add(3,6) @my_log
def run():
print('run') run() # 但添加无参函数run()时又出现了错误
# TypeError: wrapper() missing 2 required positional arguments: 'x' and 'y'
# 因run()=wrapper()未传给wrapper所必需的两个参数

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

 def my_log(func):
def wrapper(*args,**kwargs):
print('decorator works')
func(*args,**kwargs)
return wrapper #返回的只是wrapper函数对象,此时并没有运行 @my_log
def add(x,y):
print(x+y) add(3,6) @my_log
def run():
print('run') run() # 运行结果如下
# decorator works
#
# decorator works
# run

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

 def my_log(func):
def wrapper(*args,**kwargs):
print('decorator works')
func(*args,**kwargs)
return wrapper #返回的只是wrapper函数对象,此时并没有运行 @my_log
def add(x,y):
print(x+y) @my_log
def run():
print('run') print(run.__name__)
print(add.__name__) # 运行结果如下
# wrapper
# wrapper
#因run=my_log(run)=wrapper 偷偷修改了run方法和add方法的__name__属性

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

 from functools import wraps

 def my_log(func):
@wraps(func)
def wrapper(*args,**kwargs):
print('decorator works')
func(*args,**kwargs)
return wrapper #返回的只是wrapper函数对象,此时并没有运行 @my_log
def run(): # run=my_log(run)=wrapper 返回的是wrapper函数对象,未运行
print('running') run()
# run()=wrapper() print("函数run的__name__是%s"%run.__name__)
print('-'*30) @my_log # add=my_log(add)=wrapper 返回的也是wrapper函数对象
def add(a,b):
print(a+b) add(3,4)
# add(3,4)=wrapper(3,4)
print("函数add的__name__是%s"%add.__name__) # 运行结果如下
# running
# 函数run的__name__是run
# ------------------------------
# decorator works
#
# 函数add的__name__是add

被装饰函数有返回值时

 from functools import wraps

 #***************错误*************************
def login_required(func):
@wraps(func)
def wrapper(*args,**kwargs):
print('hello world')
func(*args,**kwargs)
return wrapper @login_required
def index():
return 'aaaaaaaaaaa' a=index()
print(a)
#index=login_required()=wrapper
#index()=wrapper()
# def wrapper(*args,**kwargs):
# print('hello world')
# index() 此处只是调用index函数得到'aaaaaaaaaaa' 但wrapper函数并未返回该值 # hello world
# None 可见wrapper()调用index()函数并未返回'aaaaaaaaaaa' #**************正确********************
def login_required2(func):
@wraps(func)
def wrapper(*args,**kwargs):
print('hello world')
return func(*args,**kwargs)
return wrapper @login_required2
def index():
return 'aaaaaaaaaaa' print('-'*20)
a=index()
print(a) #输出结果如下
#--------------------
# hello world
# 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=

 def red_oil(func):
print("ready to paint!") def red_wall_func():
print("red wall!") return red_wall_func @red_oil
def wall():
print("wall!") if __name__=="__main__":
print("start painting!!!")

执行结果如下:

ready to paint!

start painting!!!

 def red_oil(func):
print("ready to paint!") def red_wall_func():
print("red wall!") return red_wall_func @red_oil
def wall():
print("wall!") if __name__=="__main__":
print("start painting!!!")
wall()

执行结果如下:
  ready to paint!

start painting!!!

red wall!

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

上一篇:Robots.txt - 禁止爬虫(转)


下一篇:Android事件分发机制(上)