39
In [55]: a = 5

In [56]: b = 6

In [57]: (a, b) = (b, a)

In [58]: a
Out[58]: 6

In [59]: b
Out[59]: 5

この a と b の値の交換は内部でどのように機能しますか? 一時変数を使用していないことは間違いありません。

4

1 に答える 1

91

Python は、右側の式を左側の代入から分離します。最初に右側が評価され、結果がスタックに格納されます。次に、スタックから再び値を取得するオペコードを使用して、左側の名前が割り当てられます。

2 つまたは 3 つの項目を持つタプルの割り当ての場合、Python はスタックを直接使用します。

>>> import dis
>>> def foo(a, b):
...     a, b = b, a
... 
>>> dis.dis(foo)
  2           0 LOAD_FAST                1 (b)
              3 LOAD_FAST                0 (a)
              6 ROT_TWO             
              7 STORE_FAST               0 (a)
             10 STORE_FAST               1 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        

2 つのLOAD_FASTオペコード(変数からスタックに値をプッシュする) の後、スタックの一番上に[a, b]. ROT_TWOオペコードは、スタックの上位 2 つの位置を入れ替えて、スタックが[b, a]最上位になるようにします。次に、2 つのSTORE_FASTオペコードはこれら 2 つの値を取得し、割り当ての左側の名前に格納します。最初STORE_FASTはスタックの一番上の値をポップして に入れa、次は再びポップして値を に格納しますb。Python は、左側のターゲット リスト内の割り当てが左から右に行われることを保証するため、ローテーションが必要です。

3 つの名前の割り当ての場合、ROT_THREE続いてROT_TWOが実行され、スタックの上位 3 項目が反転されます。

より長い左側の代入の場合、明示的なタプルが構築されます。

>>> def bar(a, b, c, d):
...     d, c, b, a = a, b, c, d
... 
>>> dis.dis(bar)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 LOAD_FAST                2 (c)
              9 LOAD_FAST                3 (d)
             12 BUILD_TUPLE              4
             15 UNPACK_SEQUENCE          4
             18 STORE_FAST               3 (d)
             21 STORE_FAST               2 (c)
             24 STORE_FAST               1 (b)
             27 STORE_FAST               0 (a)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE        

ここでは、スタックを[d, c, b, a]使用してタプルを構築し (逆の順序でBUILD_TUPLE、スタックから再度ポップし、結果のタプルをスタックにプッシュします)、スタックUNPACK_SEQUENCEからタプルを再度ポップし、タプルからすべての要素をタプルにプッシュします。操作のために再度スタックしますSTORE_FAST

後者は無駄な操作のように見えるかもしれませんが、代入の右辺はまったく別のもの、おそらくタプルを生成する関数呼び出しである可能性があるため、Python インタープリターは仮定を行わず、UNPACK_SEQUENCE常にオペコードを使用します。2 つまたは 3 つの名前の代入操作でもそうしますが、後の (のぞき穴) 最適化ステップBUILD_TUPLEでは、 /のUNPACK_SEQUENCE組み合わせと 2 つまたは 3 つの引数を上記ROT_TWOおよびROT_THREEオペコードに置き換えて効率を高めます。

于 2014-01-10T15:05:47.210 に答える