14

私は python @decorator を学んだばかりです。それはクールですが、すぐに変更したコードに奇妙な問題が発生することがわかりました。

def with_wrapper(param1):
    def dummy_wrapper(fn):
        print param1
        param1 = 'new'
        fn(param1)
    return dummy_wrapper

def dummy():
    @with_wrapper('param1')
    def implementation(param2):
        print param2

dummy()

デバッグすると、print param1 で例外がスローされます

UnboundLocalError: local variable 'param1' referenced before assignment

この行を削除すると param1 = 'new'、外部スコープからの変数に対する変更操作 (新しいオブジェクトへのリンク) なしで、このルーチンが機能する可能性があります。

外側のスコープ変数のコピーを 1 つだけ作成してから変更したということですか?


Delnan に感謝します。閉鎖には不可欠です。ここからの答えの可能性: 言語 X のクロージャーと比較して、Python のクロージャーにはどのような制限がありますか?

次のようなコード:

def e(a):
    def f():
        print a
        a = '1'
    f()
e('2')

また、これは以前の迷惑なグローバル変数のようです:

a = '1'
def b():
    #global a
    print a
    a = '2'
b()

これは、グローバルシンボルを追加することで修正されます。しかし、閉鎖のために、そのようなシンボルは見つかりませんでした。unutbu に感謝します。Python 3 はnonlocalを提供してくれました。

上記から、外部変数への直接アクセスは読み取り専用であることを知っています。しかし、先行読み取り変数 (print var) も影響を受けるのを見るのはちょっと不快です。

4

3 に答える 3

19

Python が関数を解析するとき、代入の左側で使用されている変数を見つけるたびに、次のように記録します。

param1 = 'new'

そのような変数はすべて関数に対してローカルであると想定しています。したがって、この割り当ての前に

print param1

print ステートメントの実行時に Python がこのローカル変数の値を持っていないため、エラーが発生します。


Python3 では、param1非ローカルであることを宣言することでこれを修正できます。

def with_wrapper(param1):
    def dummy_wrapper(fn):
        nonlocal param1
        print param1
        param1 = 'new'
        fn(param1)
    return dummy_wrapper

Python2 では、param1 をリスト (またはその他の変更可能なオブジェクト) 内に渡すなどのトリックに頼る必要があります。

def with_wrapper(param1_list):
    def dummy_wrapper(fn):
        print param1_list[0]
        param1_list[0] = 'new'   # mutate the value inside the list
        fn(param1_list[0])
    return dummy_wrapper

def dummy():
    @with_wrapper(['param1'])   # <--- Note we pass a list here
    def implementation(param2):
        print param2
于 2012-08-29T16:11:40.550 に答える
0

ローカル変数param1を作成する関数で割り当てます。param1ただし、印刷した時点では割り当てられていないため、エラーが発生します。Pythonは、外部スコープで変数を探すことにフォールバックしません。

于 2012-08-29T16:12:56.440 に答える
0

一時的な状況や探索的なコードに便利です (ただし、そうでなければ良い習慣ではありません) :

Python 2.x で外側のスコープから変数を取得したい場合は、global を使用することもできます (通常の条件付きで)。

以下がスローされますが (inner 内での outer1 の代入によりローカルになり、if 条件で無制限になります):

def outer():
    outer1 = 1
    def inner():
        if outer1 == 1:
            outer1 = 2
            print('attempted to accessed outer %d' % outer1)

これはしません:

def outer():
    global outer1
    outer1 = 1
    def inner():
        global outer1
        if outer1 == 1:
            outer1 = 2
            print('accessed outer %d' % outer1)
于 2015-07-17T13:29:53.823 に答える