我有一个装饰器来验证我使用请求获得的json响应.我还将一些请求逻辑包装到一个类中,该类接受装饰器需要验证的模式.我尝试在类的get函数上使用装饰器,但每次尝试执行此操作时都会出现类型错误:
TypeError: get() takes exactly 1 argument (0 given)
我知道如果你在类方法上使用装饰器,你需要在装饰器中设置self = None.
这是代码:
schema = {'pid': int,'description': str} def validator(schema): def _validator(f): @wraps(f) def wrapped(self=None): print f check = all([isinstance(v,schema[k]) for k,v in f().iteritems() if v]) return f() if check else None return wrapped return _validator class Data(object): ''' Base request wrapper to use for all apis accepts a validator schema to check the type of response values. ''' def __init__(self,base_url,schema=None,debug=False): self.base_url = base_url self.schema = schema def __getattr__(self,key): new_base = self.append_to_url(self.base_url,key) return self.__class__(base_url=new_base) def __getitem__(self,key): return self.__getattr__(key) # def __call__(self,**kwargs): # self.base_url = self.base_url[:-1] # return self.get(self.base_url,**kwargs) def append_to_url(self,param): return '{}{}/'.format(base_url,param) @validator(schema) def get(self,**kwargs): try: r = requests.get(self.base_url[:-1],**kwargs) r.raise_for_status() return r.json() except requests.exceptions.ConnectionError as e: raise errors.ApiError(e) except requests.exceptions.HTTPError as e: raise errors.ApiError(e) product_details = Data('my_api_url',schema).shoes['10'].get()
我认为这是因为在我的验证器中我初始化f(),这是get函数期待自我.我试图在装饰器中将self传递给f()但是也会产生相同的错误.我甚至试图在__call__方法上初始化get函数,但是得到get函数需要1 arg和2.
必须有一种更有效或更加方便的方法来实现这一目标.我知道有一个名为妖娆的图书馆可以进行大量的验证,但我想尝试一些简单的学习练习.
解决方法
这不是被包裹的函数调用失败,而是获取调用.你调用它的所有地方就像f(),即没有任何参数.
import functools def validator(schema): def _validator(f): @functools.wraps(f) def wrapper(self): # pass self to the wrapped function result = f(self) return 'wrapped {} {}'.format(schema,result) return wrapper return _validator class Data(object): @validator('test') def get(self): return 'data' print(Data().get()) # wrapped test data
虽然您可以直接使用自我或其他此类特定参数,但使用* args和** kwargs会使装饰器更加灵活,例如使它也可用于非约束函数:
import functools def validator(schema): def _validator(f): @functools.wraps(f) def wrapped(*args,**kwargs): # pass aribitrary args to the wrapped function # for greater flexibility result = f(*args,**kwargs) return 'wrapped {} {}'.format(schema,result) return wrapped return _validator class Data(object): @validator('test') def get(self): return 'data' @validator('test2') def get(): return 'data2' print(Data().get()) # wrapped test data print(get()) # wrapped test2 data2