我刚开始玩Python中的装饰器。我目前无法找出处理此用例的最佳方法。由于装饰函数无法从装饰器访问局部变量。
schema = {
'lang': {'type': 'string', 'required':True}
}
def validate(schema):
def my_decorator(func):
def wrapper(*args, **kwargs):
# Read from request body args[1]
# Validate the json is in fact correct
valid_json = json.loads(body.decode('utf-8'))
# Compare valid_json to the schema using "Cerberus"
return args, kwargs
return wrapper
return my_decorator
@validate(schema)
def main_function(self, req, resp):
# call database with valid_json
我的问题是:如何从我的装饰函数访问valid_json,以便能够在之后插入我的数据库。这种情况的最佳做法是什么?
编辑:我正在运行pypy 2.4
答案 0 :(得分:1)
IIUC你问的是什么,以下可以这样做。我不能说我对这个解决方案感到疯狂(或者你要问的是什么) - 这对我来说太过“神奇”。
为简单起见,我们假设模式和所有内容都是整数,并且通过模式验证某些内容只是意味着向其添加模式整数(这仅用于说明)。所以我们有:
def validate_schema(schema, arg):
return arg + schema
现在我们可以编写以下装饰器:
def validate(schema, arg_pos):
"""
Decorates a function so that the arg_pos argument will
be modified to be validated by schema.
"""
def decorator(method):
@functools.wraps(method)
def f(*args, **kwargs):
# Manipulate args to modify the validated argument:
args = args[: arg_pos] + (validate_schema(schema, args[arg_pos]), ) + args[arg_pos + 1: ]
# Call the method with the modified arguments
return method(*args, **kwargs)
return f
return decorator
现在我们可以这样使用它:
# Validate arg #0 with schema 12
@validate(12, 0)
def bar(inp_):
print inp_
>>> bar(1)
13
当然,您可以进一步详细说明,采用模式+参数对,处理关键字参数等等。不过,这就是这个答案的原则。我认为这有点有趣,但我不会用它。
答案 1 :(得分:1)
你不能直接这样做。被装饰的函数的范围在它被装饰器包装之前设置,并且它与装饰器添加的包装器建立的范围没有任何关联。
你能做的最好的事情是让函数接受装饰者传递的其他参数,例如:
a=group.sort_values('datetime',ascending=True)
r=np.arange(1,a.shape[0]+1)
points = pandas.DataFrame(r,columns=['points'])
a = pandas.concat([a,points],axis=1)
装饰器在调用包装函数时显式添加Traceback (most recent call last):
File "C:/Users/wymeka/Documents/ENSAE/DataViz_twitter/org/ensae/dataviz/scraping/utils/json_parsing.py", line 123, in <module>
print(fastest_tweet_qag())
File "C:/Users/wymeka/Documents/ENSAE/DataViz_twitter/org/ensae/dataviz/scraping/utils/json_parsing.py", line 110, in fastest_tweet_qag
groups.apply(fastest)
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\groupby.py", line 713, in apply
return self._python_apply_general(f)
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\groupby.py", line 717, in _python_apply_general
self.axis)
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\groupby.py", line 1347, in apply
res = f(group)
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\groupby.py", line 709, in f
return func(g, *args, **kwargs)
File "C:/Users/wymeka/Documents/ENSAE/DataViz_twitter/org/ensae/dataviz/scraping/utils/json_parsing.py", line 92, in fastest
group=pandas.concat([group,points],axis=1)
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\tools\merge.py", line 810, in concat
return op.get_result()
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\tools\merge.py", line 992, in get_result
mgrs_indexers, self.new_axes, concat_axis=self.axis, copy=self.copy)
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\internals.py", line 4277, in concatenate_block_managers
for placement, join_units in concat_plan]
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\internals.py", line 4277, in <listcomp>
for placement, join_units in concat_plan]
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\internals.py", line 4370, in concatenate_join_units
empty_dtype, upcasted_na = get_empty_dtype_and_na(join_units)
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\internals.py", line 4305, in get_empty_dtype_and_na
dtypes[i] = unit.dtype
File "pandas\src\properties.pyx", line 34, in pandas.lib.cache_readonly.__get__ (pandas\lib.c:43950)
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\internals.py", line 4584, in dtype
self.block.fill_value)[0])
File "C:\Users\wymeka\Documents\Python\python\lib\site-packages\pandas\core\common.py", line 1103, in _maybe_promote
elif issubclass(np.dtype(dtype).type, compat.string_types):
TypeError: data type not understood
,例如@validate(schema)
def main_function(self, req, resp, valid_json=None):
# call database with valid_json
。
答案 2 :(得分:1)
我最近也和装饰师一起玩,我的印象是,这并不是一个好方法。您无法从函数外部访问函数的本地范围。
使用部分功能应用程序和功能成员数据的潜在解决方案,尽管不是您想要的,但可能是:
from functools import wraps
schema = { 'lang': {'type': 'string', 'required':True} }
def compare(schema, json):
return schema == json
def validate(schema):
def outer(function):
@wraps(function)
def inner(*args, **kwargs):
return function(*args, **kwargs)
inner.validate = lambda json: compare(schema, json)
return inner
return outer
@validate(schema)
def main_function():
print main_function.validate({ 'lang': {'type': 'string', 'required':True} })
print main_function.validate({})
main_function()
# Output:
# True
# False
compare
函数显然可以自定义以执行有用的操作,但您仍需要在main_function
内验证json。我不知道在main_function
内没有内部函数的情况下是否有办法解决这个问题。
也可以&#34;注射&#34;将变量放入函数的全局范围并使用装饰器进行必要的簿记:
from functools import wraps
schema = {
'lang': {'type': 'string', 'required':True}
}
def validate(schema):
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
_backup, _check = None, False
if 'valid_json' in func.func_globals:
_backup, _check = func.func_globals['valid_json'], True
valid_json = {'hi': 'mom'} # or whatever
func.func_globals['valid_json'] = valid_json
output = func(*args, **kwargs)
if _check:
func.func_globals['valid_json'] = _backup
else:
del func.func_globals['valid_json']
return output
return wrapper
return my_decorator
@validate(schema)
def main_function():
print valid_json
main_function()
# Output:
# {'hi': 'mom'}