5

次のコードスニペットがあります。

def isolation_level(level):
    def decorator(fn):
        def recur(level, *args, **kwargs):
            if connection.inside_block:
                if connection.isolation_level < level:
                    raise IsolationLevelError(connection)
                else:
                    fn(*args, **kwargs)
            else:
                connection.enter_block()
                try:
                    connection.set_isolation_level(level)
                    fn(*args, **kwargs)
                    connection.commit()
                except IsolationLevelError, e:
                    connection.rollback()
                    recur(e.level, *args, **kwargs)
                finally:
                    connection.leave_block()
        def newfn(*args, **kwargs):
            if level is None: # <<<< ERROR MESSAGE HERE, Unbound local variable `level`
                if len(args):
                    if hasattr(args[0], 'isolation_level'):
                        level = args[0].isolation_level
                elif kwargs.has_key('self'):
                    if hasattr(kwargs['self'], 'isolation_level'):
                        level = kwargs.pop('self', 1) 
            if connection.is_dirty():
                connection.commit()
            recur(level, *args, **kwargs)
        return newfn
    return decorator

それが何をするかは本当に問題ではありませんが、私はそれを元の形で投稿します。それ以上の単純なものでは状況を再現することができなかったからです。

問題は、電話をかけるisolation_level(1)(some_func)(some, args, here)Unbound local variable21行目(リストにマークされている)で例外が発生することです。理由がわかりません。何が悪いのかを理解するために、実装の詳細がすべて含まれていない関数と関数呼び出しの同じ構造を再作成してみました。ただし、その場合は例外メッセージが表示されません。たとえば、次のように機能します。

def outer(x=None):
    def outer2(y):
        def inner(x, *args, **kwargs):
            print x
            print y
            print args
            print kwargs
        def inner2(*args, **kwargs):
            if x is None:
                print "I'm confused"
            inner(x, *args, **kwargs)
        return inner2
    return outer2

outer(1)(2)(3, z=4)

プリント:

1
2
(3,)
{'z': 4}

何が足りないの?

編集

さて、問題は、最初のバージョンで実際に変数への割り当てを実行することです。Pythonはそれを検出するため、変数がローカルであると想定します。

4

1 に答える 1

9

ローカル変数はコンパイル時に決定されます。levelエラーが発生した行の数行下の変数への割り当てにより、その変数は内部関数に対してローカルになります。だからライン

if level is None:

実際には最も内側のスコープの変数にアクセスしようとしますlevelが、そのような変数はまだ存在していません。Python 3.xでは、次のように宣言することでこの問題を解決できます。

nonlocal level

内部関数の最初で、実際に外部関数の変数を変更したい場合。それ以外の場合は、内部関数で別の変数名を使用できます。

于 2011-04-29T22:34:48.407 に答える