1

次のデコレータがあります。

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}

2 番目の 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 で引数がなくなった理由を知っていますか? timeoutと引数にアクセスできるため、さらに奇妙ですretry(ネストされた関数ではこれが通常の動作であるにもかかわらず)。

回避策があります:

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

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

しかし、それは解決策ではありません (引数keyは にまだありますが_my_decoratorkey2アクセスできるようになりました)

Python バージョン: 2.7.3

4

2 に答える 2

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__
        ...
于 2012-10-31T10:51:09.853 に答える
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

于 2012-10-31T10:50:19.260 に答える