python 装饰器的缺点以及解决方法
1.python装饰器的缺点
装饰器可以允许我们在不改变函数或犯方法的调用方式的情况下,添加额外的功能;
如下所示,我们要在中的方法之前增加装饰器check_is_admin,用来判断执行类的方法的用户是否为admin用户;
def check_is_admin(f): def wrapper(*args,**kwargs): if kwargs.get('username') != 'admin': raise Exception("This user is not allowed to get food") return f(*args,**kwargs) return wrapper class store(object): @check_is_admin def get_food(self,username,food): return self.storage.get(food) @check_is_admin def put_food(self,username,food): self.storage.put(food)
但是,经过装饰器修饰的函数,其func_name和func_doc的属性都会丢失;
如下:
def foobar(username="someone"): """Do crzay staff.""" pass print foobar.func_doc print foobar.func_name 执行结果为: Do crzay staff. foobar
如果给上述函数增加装饰器呢?
def is_admin(f): def wrapper(*args,**kwargs): if kwargs.get(*args,**kwargs): raise Exception("This user is not allowed to get food") return f(*args,**kwargs) return wrapper @is_admin def foobar(username="someone"): """Do crazy staff""" pass print foobar.__doc__ print foobar.__name__ 执行结果为: None wrapper
update_wapper的源码如下:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): """Update a wrapper function to look like the wrapped function wrapper is the function to be updated wrapped is the original function assigned is a tuple naming the attributes assigned directly from the wrapped function to the wrapper function (defaults to functools.WRAPPER_ASSIGNMENTS) updated is a tuple naming the attributes of the wrapper that are updated with the corresponding attribute from the wrapped function (defaults to functools.WRAPPER_UPDATES) """ for attr in assigned: setattr(wrapper, attr, getattr(wrapped, attr)) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # Return the wrapper so this can be used as a decorator via partial() return wrapper
2.解决方案
使用functools的update_wrapper函数可以解决该问题
如下:
def is_admin(f): def wrapper(*args,**kwargs): if kwargs.get(*args,**kwargs): raise Exception("This user is not allowed to get food") return f(*args,**kwargs) return wrapper def foobar(username="someone"): """Do crazy staff""" pass import functools foobar = functools.update_wrapper(is_admin,foobar) print foobar.__name__ print foobar.__doc__ 执行结果如下: foobar Do crazy staff
但是上述方式手工调用装饰器不太方便,我们在这里使用functools.warps的装饰器
import functools def is_admin(f): @functools.wraps(f) def wrapper(*args,**kwargs): if kwargs.get(*args,**kwargs): raise Exception("This user is not allowed to get food") return f(*args,**kwargs) return wrapper @is_admin def foobar(username="someone"): """Do crazy staff""" pass print foobar.__doc__ print foobar.__name__ 执行结果为: Do crazy staff ##### foobar
结论:python的装饰器会导致被修饰函数的__doc__,__name__等属性丢掉,如果要保留函数的这些属性,需要在装饰器函数中添加functools.wrap装饰器;