6

あまり動的でない C++ から来たので、この Python (2.7) コードの動作を理解するのに苦労しています。

注:これが悪いプログラミングスタイル/悪であることは承知していますが、それでも理解したいと思います。

vals = [1,2,3]

def f():
    vals[0] = 5
    print 'inside', vals

print 'outside', vals
f()
print 'outside', vals

このコードはエラーなしで実行されf、(一見) グローバル リストを操作します。これは、関数内で操作される (読み取るだけでなく) グローバル変数は として宣言する必要があるという私の以前の理解に反していますglobal ...

一方、 に置き換えるvals[0] = 5と、 に aを追加しない限りvals += [5,6]、実行は an で失敗します。これは、最初のケースでも起こると予想していたことです。UnboundLocalErrorglobal valsf

この振る舞いを説明できますか?

vals最初のケースで操作できるのはなぜですか? 最初のタイプの操作は失敗しないのに、2 番目のタイプの操作は失敗するのはなぜですか?

更新:vals.extend(...)なしで機能 するコメントで指摘されましたglobal。これは私の混乱を助長します -+=への呼び出しとは異なる扱いを受けるのはなぜextendですか?

4

3 に答える 3

6

global変数が参照するオブジェクトを変更しようとしている場合にのみ必要です。vals[0] = 5参照ではなく実際のオブジェクトを変更するため、エラーは発生しません。ただし、vals += [5, 6]グローバル変数を変更できないため、インタープリターはローカル変数を見つけようとします。

紛らわしいのは、 list で+=演算子を使用すると、 のように元のリストが変更されることvals[0] = 5です。そして、vals += [5, 6]失敗してもうまくいきますvals.extend([5, 6])。の助けを借りて、dis.disいくつかの手がかりを貸してもらうことができます。

>>> def a(): v[0] = 1
>>> def b(): v += [1]
>>> def c(): v.extend([1])
>>> import dis
>>> dis.dis(a)
  1           0 LOAD_CONST               1 (1)
              3 LOAD_GLOBAL              0 (v)
              6 LOAD_CONST               2 (0)
              9 STORE_SUBSCR        
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE        
>>> dis.dis(b)
  1           0 LOAD_FAST                0 (v)
              3 LOAD_CONST               1 (1)
              6 BUILD_LIST               1
              9 INPLACE_ADD         
             10 STORE_FAST               0 (v)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        
d
>>> dis.dis(c)
  1           0 LOAD_GLOBAL              0 (v)
              3 LOAD_ATTR                1 (extend)
              6 LOAD_CONST               1 (1)
              9 BUILD_LIST               1
             12 CALL_FUNCTION            1
             15 POP_TOP             
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE

関数aと をc使用LOAD_GLOBALしているのに対し、bを使用しようとしていることがわかりますLOAD_FAST。using が機能しない理由がわかり+=ました。インタープリターはv、インプレース加算のデフォルトの動作であるため、ローカル変数としてロードしようとします。がリストかどうかわからないvので、基本的に行が と同じ意味であると仮定しv = v + [1]ます。

于 2013-12-25T10:29:03.953 に答える
2

global外側のスコープの変数に割り当てたい場合に必要です。を使用しない場合global、Python はvals割り当てを行うときにローカル変数と見なします。

+=は割り当て (拡張された割り当て) であり、vals += [5, 6]を読み取ることと同等でありvals、その値に追加[5, 6]して、結果のリストを元の に割り当てますvalsvals += [5,6]has ステートメントがないためglobal、Python は割り当てを認識し、valsローカルとして扱います。と呼ばれるローカル変数を作成しませんでしたvalsが、それに追加しようとし、ここからUnboundLocalError.

ただし、読み取りには を使用する必要はありませんglobal。変数は最初にローカルで検索され、ローカル スコープで見つからない場合は、外側のスコープで検索されます。また、参照型を扱っているため、読み取りを行うと参照が返されます。その参照を通じて、オブジェクトの内容を変更できます。

これが機能する理由.extend()です (参照で呼び出され、オブジェクト自体に作用するvals += [5, 6]ため) が失敗します (valsローカルでもマークされていないためglobal)。

試してみる変更例を次に示します (ローカルを使用するとvalsがクリアされますUnboundLocalError)。

vals = [1, 2, 3]

def f():
    vals = []
    vals += [5,6]
    print 'inside', vals

print 'outside', vals
f()
print 'outside', vals
于 2013-12-25T10:07:48.270 に答える