Python进阶精华编写装饰器为被包装的函数添加参数
此方法主要目的就是为了给被包装函数添加额外参数:实例如下:from functools import wraps def option_debug(func): @wraps(func) def wrapper(*args,debug=False,**kwargs): if debug: print("Calling:",func.__name__) return func(*args,**kwargs) return wrapper @option_debug def add(x,y,z): return x+y+z print(add(1,2,3,debug=True)) #理解装饰器,实质上就只要理解了下述代码,就不难了: f = option_debug(add)(1,2,3,debug=True) print(f)
注意:这种发方法并不是装饰器最常用的功能,但是在降低代码重复上可谓是首屈一指。比如:如果不使用装饰器,上述代码可能会很多:def add(x,y,z,debug=False): if debug: print("calling:") return x+y+z def sub(x,y,z,debug=False): if debug: print("calling:") return x-y-z def mult(x,y,z,debug=False): if debug: print("calling:") return x*y*z #等等诸多函数,都需要一个一个的去设置debug的情况, #而有了装饰器,这些内容仅仅需要写一次就可以在很多函数中使用。
当然,这里也有一个潜在的风险,就是当装饰器包裹的函数已经用了debug作为参数名,那么装饰器这里将会报错,所以要添加额外的一些判断来完善代码:from functools import wraps import inspect def option_debug(func): if "debug" in inspect.getargspec(func).args: raise TypeError("debug argument already defined") @wraps(func) def wrapper(*args,debug=False,**kwargs): if debug: print("Calling:",func.__name__) return func(*args,**kwargs) return wrapper @option_debug def add(x,y,debug): return x+y,debug print(1,2,"True") """ # 会出现如下异常: Traceback (most recent call last): File "F:/PycharmProjects/class_obj/class_one.py", line 962, in @option_debug File "F:/PycharmProjects/class_obj/class_one.py", line 954, in option_debug raise TypeError("debug argument already defined") TypeError: debug argument already defined (该参数已经被在装饰器中定义了,不能重复定义) Process finished with exit code 1 """
最后还剩下一部分比较难理解的地方,我将理解的注释在每行代码上方,这个问题就是,在打印被修饰函数的参数签名时,其实并不能正确显示参数签名,原因是因为被wrapper修饰过后的函数实际上应该使用的是wrapper的参数签名表,例如:@option_debug def add(x,y): return x+y print(inspect.signature(add)) # (x, y) 实际上应该是(x,y,*,debug=False)
所以,接下来,完成最后最难的一步:from functools import wraps import inspect def option_debug(func): if "debug" in inspect.getargspec(func).args: raise TypeError("debug argument already defined") @wraps(func) def wrapper(*args,debug=False,**kwargs): if debug: print("Calling:",func.__name__) return func(*args,**kwargs) #获取修饰的函数的参数签名表并且返回一个有序字典(顺序依照实际函数参数顺序)对象 #这个字典本质上是有一个存放了元组(元组中存放的是参数名和参数地址两个属性)的列表通过Orderdict转化而来 sig = inspect.signature(func)#signature允许我们从函数中抽取参数签名出来 parms = list(sig.parameters.values())#将参数信息表的值列表化 #sig.paramenters实际就是字典,通过键(参数名)可以找到该参数所有的属性例如下一行中: parms.append(inspect.Parameter("debug",kind=inspect.Parameter.KEYWORD_ONLY,default=False))#追加一个新的参数信息进去 wrapper.__signature__ = sig.replace(parameters=parms)#替换原有参数信息,并且修改wrapper的参数签名表 return wrapper @option_debug def add(x,y): return x+y print(inspect.signature(add)) # (x, y, *, debug=False)