python函数之有参装饰器
一、为什么要有有参装饰器?
来看之前的无参装饰器
# 无参装饰器 def outter(func): def wrapper(*args,**kwargs): start = time.time() res = func(*args,**kwargs) # 我们需要一个变量接受函数的返回值 end = time.time() print("run time is %s" % (end - start)) return res # 我们装饰器的核心是wrapper函数,只需要在wrapper函数中提供返回值即可 return wrapper @outter # @+装饰器名称,可以快速完成 pint1 = outter(print1)的函数赋值 def print1(name): print(name) time.sleep(1) return 111
可以看到,我们从outter()和wrapper()向内层函数进行了传参,outter函数传入的是我们所要修饰的函数对象,wrapper则是将参数原封不动的传给了修饰的函数对象,如果现在有个新的需求:
- 设计一个装饰器,为函数增加认证功能,其中登陆的认证来源有多个(file、mysql)
首先看一下无参装饰器能否实现:
# 认证功能装饰器 def outter(func): def wrapper(*args,**kwargs): name = input("请输入用户名>>>") pwd = input("请输入密码>>>") if mode == "1": # 需要通过mode进行判断信息来源,但是没有参数传入 if name == "zhangda" and pwd == "123": print("登陆成功!") res1 = func(*args,**kwargs) return res1 else: print("用户名或者密码错误") elif mode == "2": #需要通过mode进行判断信息来源,但是没有参数传入
print("认证来源:mysql")
else:
print("未知来源")
return wrapper
我们需要传入一个mode参数进行判断,但是wrapper()需要给func()原样传入参数,所以不能添加mode,外层的outter()可以为mode传参,但是由于python语法糖限制,里面除了func外,不能添加其他的参数,那这个mode参数怎么传入呢?
我们可以在最外层函数进行传入
# 认证功能装饰器 def outter(func): mode = "file" def wrapper(*args,**kwargs): name = input("请输入用户名>>>") pwd = input("请输入密码>>>") if mode == "1": if name == "zhangda" and pwd == "123": print("登陆成功!") res1 = func(*args,**kwargs) return res1 else: print("用户名或者密码错误") elif mode == "2": print("认证来源:mysql") else: print("未知来源") return wrapper @outter def f1(): print("hhhhh")
但是这样每次使用装饰器都需要修改,并且不能同时满足多个信息来源需求,回想之前给函数传参的两种方法,既然直接传参行不通,我们想到了利用闭包函数特性,再包一层函数,这就是有参装饰器
def outter2(mode="file"): def outter(func): def wrapper(*args,**kwargs): name = input("Username:").strip() pwd = input("Password:").strip() if mode == "file": if name == "egon" and pwd == "123": print("登陆成功!") res = func(*args,**kwargs) return res else: print("登陆失败!") elif mode == "mysql": print("基于mysql认证!") else: print("未知来源认证!") return wrapper return outter @outter2("mysql") def index(name): print("{}".format(name)) index("hhhhhh")
在这里将之前的装饰器outter看做一个整体,对他进行装饰,并且最外层函数可以传入任意参数的,有参装饰的语法糖:@函数名(参数1,参数2......)
二、装饰器wraps
我们知道,装饰器其实就是定义了一个闭包函数,包含了原来的函数与功能并且添加新功能,最后通过函数对象赋值实现了全局调用,但是函数内存地址已经改变,只是名称看着一样
def outter(func): def wrapper(*args,**kwargs): """wrapper注释""" print("11111") res = func(*args,**kwargs) return res return wrapper @outter def index(): """index注释""" print("2222") print(index.__name__) # 打印函数的名称 print(index.__doc__) # 打印函数的注释 print(index) # 打印函数内存地址
运行结果如下:

index只是披着index外衣的wrapper函数,他的本质其实是wrapper,这不会影响函数的原始使用,但是如果有需求,希望这些属性看着不变呢?
def outter(func): def wrapper(*args,**kwargs): """wrapper注释""" print("11111") res = func(*args,**kwargs) return res wrapper.__name__ = func.__name__ wrapper.__doc__ = func.__doc__ return wrapper @outter def index(): """index注释""" print("2222") print(index.__name__) # 打印函数的名称 print(index.__doc__) # 打印函数的注释 print(index) # 打印函数内存地址
我们可以通过将wrapper的名称等信息重新复制,将原函数的信息赋值给wrapper即可:

其实python内置了一个库functools,提供了装饰器实现这一功能,用法如下:
from functools import wraps def outter(func): @wraps(func) def wrapper(*args,**kwargs): """wrapper注释""" print("11111") res = func(*args,**kwargs) return res # wrapper.__name__ = func.__name__ # wrapper.__doc__ = func.__doc__ return wrapper @outter def index(): """index注释""" print("2222") print(index.__name__) # 打印函数的名称 print(index.__doc__) # 打印函数的注释 print(index) # 打印函数内存地址
三、总结
装饰器模板
- 无参装饰器
def outter(func): def wrapper(*args,**kwargs): res = func(*args,**kwargs) return res return wrapper
- 有参修饰器
def outter2(x): def outter(func): def wrapper(*args,**kwargs): print(x) res = func(*args,**kwargs) return res return wrapper return outter @outter2("1") def f1(): print("2")

浙公网安备 33010602011771号