6

私は他のいくつかの SO ( PythonScopeglobals は global を必要としません) を読みましたが、私が望むほど明確に説明しているようには見えず、 PyDocsが私の質問への答えを教えてくれるかどうかを精神的にふるいにかけるのに苦労しています:

myList = [1]

def foo():
    myList = myList + [2, 3]
def bar():
    myList.extend([2, 3])
def baz():
    myList += [2, 3]

さて、当然のことながら、

>>> foo()
UnboundLocalError: local variable 'myList' referenced before assignment

bar()  # works
myList # shows [1, 2, 3]

しかしその後

>>> baz()
UnboundLocalError: local variable 'myList' referenced before assignment

+=しかし、この場合は暗黙のうちにメソッド演算子を呼び出すようなものだと思いましたが、エラーは、何らかの理由で実際にはとしてextend()扱わないことを意味します。これは、Python の解析がどのように機能するべきかということと一致していますか?+=extends()

メソッド演算子と同等の関数を呼び出すと、すべての場合で同等になると思いました。+=代わりに、実際の代入演算子として扱っているようです。ただし、これは完全に真実ではありません。

myList = range(50000000) # wait a second or two on my laptop before returning
myList += [0]            # returns instantly
myList = myList + [1]    # wait a second or two before returning

+=実際に を呼び出すだけであれば、これらすべてが期待されますextend()

をローカル変数として扱う必要があり、グローバルを認識するようなものに暗黙的に変換できないことを明確にmyListする、私が見逃しているいくつかの細かい区別(または非常に明白な点...)はありますか?変数?baz()+=extend()

4

2 に答える 2

4

+=を暗黙的に呼び出しませんextend()。まず、増補代入演算子です。

そのセクションを見ると、次のように書かれassignmentています。

単一のターゲットへのオブジェクトの割り当ては、次のように再帰的に定義されます。

ターゲットが識別子 (名前) の場合:

名前が現在のコード ブロックのグローバル ステートメントに出現しない場合: 名前は、現在のローカル名前空間のオブジェクトにバインドされます。それ以外の場合: 名前は現在のグローバル名前空間のオブジェクトにバインドされます。

拡張割り当ては次のとおりです。

拡張代入は、1 つのステートメントで、2 項演算と代入ステートメントを組み合わせたものです。

同じルールでプレイします。ご覧のように:

>>> def baz():
        myList += [2, 3]


>>> dis.dis(baz)
  2           0 LOAD_FAST                0 (myList)
              3 LOAD_CONST               1 (2)
              6 LOAD_CONST               2 (3)
              9 BUILD_LIST               2
             12 INPLACE_ADD         
             13 STORE_FAST               0 (myList)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE  

拡張代入は、ターゲット (通常の代入ステートメントとは異なり、アンパックにはなりません) と式リストを評価し、2 つのオペランドで代入のタイプに固有のバイナリ演算を実行し、結果を元のターゲットに代入します。ターゲットは 1 回だけ評価されます。

最初の呼び出しは を評価しようとします。これは、ローカル変数であると想定されるステートメントがなかったためmyList、次のようになります。LOAD_FASTglobal

LOAD_FAST(var_num)

ローカル への参照をco_varnames[var_num]スタックにプッシュします。

見つからないため、エラーが発生します。見つかった場合、拡張のジョブを実行するメソッドを呼び出すoppcodeに到達します。この操作が完了すると、結果が変数に割り当てられますが、ここまで到達することはありません。INPLACE_ADDmyList.__iadd__

とにかく s を実際に操作するべきではありませんglobal。関数から新しい結果を返すか、パラメーターとして渡します。

于 2013-04-21T04:21:12.120 に答える