如何从装饰器中访问装饰函数中的局部变量

时间:2016-03-02 17:36:24

标签: python python-decorators

我刚开始玩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

3 个答案:

答案 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'}