嵌套函数中缺少参数(装饰器)

时间:2012-10-31 10:44:03

标签: python python-2.7 decorator

我有一个以下的装饰者:

from decorator import decorator
def my_decorator(key=None, timeout=None, retry=0):
    """
    My decorator
    """
    import pdb; pdb.set_trace()

    def _my_decorator(func):
        import pdb; pdb.set_trace()
        key = key or func.__name__

        @decorator
        def __my_decorator(f, *args, **kwargs):
            result = "abc"
            return result
        return __my_decorator(func)
    return _my_decorator

在第一个pdb部分中,locals()的结果是:

>>> locals()
{'key': None, 'retry': 0, 'pdb': <module 'pdb' from '/opt/python/2.7/lib/python2.7/pdb.pyc'>, 'timeout': None}

在第二个pdb部分中,locals()的结果是:

>>> locals()
{'timeout': None, 'retry': 0, 'pdb': <module 'pdb' from '/opt/python/2.7/lib/python2.7/pdb.pyc'>, 'func': <function get_items at 0x9e172cc>}

没有pdb的异常:

key = key or func.__name__
UnboundLocalError: local variable 'key' referenced before assignment

你知道为什么key参数在嵌套函数_my_decorator中消失了吗?这更奇怪,因为timeoutretry参数仍然可访问(尽管这是嵌套函数中的正常行为)。

有一种解决方法:

def my_decorator(key=None, timeout=None, retry=0):
    """
    My decorator
    """
    key2 = key

    def _my_decorator(func):
        key = key2 or func.__name__
    ...

但它不是解决方案(key中的参数_my_decorator仍在消失,但现在可以访问key2

Python版本:2.7.3

2 个答案:

答案 0 :(得分:4)

如果在内部作用域中绑定变量名,则它将从外部作用域的闭包中省略。这是因为否则后续代码将不知道引用哪个绑定:

def outer(x=None):
    def inner(y=0):
        if y:
            x = y
        return x    # outer.x or inner.x?

修复方法是重命名变量,使它们不影响封闭范围:

def my_decorator(key=None, timeout=None, retry=0):
    def _my_decorator(func):
        func_key = key or func.__name__
        ...

答案 1 :(得分:1)

您正在将分配给 key,这会使该变量成为本地变量。使用Python 2无法实现您想要实现的目标(在Python 3中,您可以将其标记为nonlocal)。

解决方法是使key变为可变,然后改变它的内容而不是分配给它:

from decorator import decorator
def my_decorator(key=None, timeout=None, retry=0):
    """
    My decorator
    """
    key = [key]

    def _my_decorator(func):
        key[0] = key[0] or func.__name__

现在我们变异 key,而不是分配给变量。换句话说,我们正在执行道德等同于key.__setitem__(0, key[0] or func.__name__),而你的代码正在执行locals()['key'] = key or func.__name,这是一种将key标记为局部变量的行为。