3

リストと別の参照リストの間で共通の要素を最初の要素と交換して、リストを変更してみました。実装は次のとおりです。

>>> L = [1,2,3,4,5,6,7,8,9]
>>> A = [3]
>>> L[0], L[L.index(A[0])] = L[L.index(A[0])], L[0] #want to swap 3 with 1  
>>> L 
[1,2,3,4,5,6,7,8,9,] #List L was not mutated  

私が予想したように、リストは変更されませんでした。しかし、以下に示すように実装を変更すると、機能しました。

>>> L = [1,2,3,4,5,6,7,8,9]
>>> A = [3]
>>> i = L.index(A[0])
>>> L[0], L[i] = L[i], L[0]
>>> L
[3,2,1,4,5,6,7,8,9,] #Now list mutated as desired even though L[i] and L[L.index(A[0])] evaluate to same value.  

私の質問は、なぜ最初の割り当てでリストを変更できなかったのかということです。私はそれについて考えましたが、私の脳はそれを説明していません。

4

2 に答える 2

12

Python では、複数の代入を行う場合、右側が最初に評価されますが、左側の代入ターゲットは、それらに式が含まれている場合、代入時に 1 つずつ評価されます。

代わりに、期待どおりに最初に割り当てターゲットとして評価される場合、これはもちろん機能します。

これは、割り当てステートメントのセクションに記載されています。

割り当てステートメントは式リストを評価し (これは単一の式またはコンマ区切りのリストであり、後者はタプルを生成することに注意してください)、単一の結果オブジェクトを各ターゲット リストに左から右に割り当てます。

ターゲット リストがターゲットのコンマ区切りリストの場合: オブジェクトは、ターゲット リスト内のターゲットと同じ数のアイテムを持つ iterable である必要があり、アイテムは左から右に対応する target に割り当てられます。

鉱山を強調します。ここでは左から右が重要です。L[0]が に割り当てられる前にL[L.index(3)]が割り当てられます。

L[0]ドキュメントでは、やなどのサブスクリプション ターゲットに何が起こるかについて詳しく説明していますL[L.index(3)]

ターゲットがサブスクリプションの場合: 参照内のプライマリ式が評価されます。変更可能なシーケンス オブジェクト (リストなど) またはマッピング オブジェクト (辞書など) のいずれかを生成する必要があります。次に、添字式が評価されます。

繰り返しますが、私のものを強調してください。添字式は個別に評価され、ターゲット リストは左から右に評価されるため、その評価はへの前の割り当ての後にL[0]行われます。

これは、Python コードを逆アセンブルすることで確認できます。

>>> import dis
>>> def f(L):
...     L[0], L[2] = L[2], L[0]
... 
>>> def g(L):
...     L[0], L[L.index(3)] = L[L.index(3)], L[0]
... 
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (L)   # L[2]
              3 LOAD_CONST               1 (2)
              6 BINARY_SUBSCR       
              7 LOAD_FAST                0 (L)   # L[0]
             10 LOAD_CONST               2 (0)
             13 BINARY_SUBSCR       
             14 ROT_TWO             
             15 LOAD_FAST                0 (L)   # Store in L[0]
             18 LOAD_CONST               2 (0)
             21 STORE_SUBSCR        
             22 LOAD_FAST                0 (L)   # Store in L[2]
             25 LOAD_CONST               1 (2)
             28 STORE_SUBSCR        
             29 LOAD_CONST               0 (None)
             32 RETURN_VALUE        
>>> dis.dis(g)
  2           0 LOAD_FAST                0 (L)   # L[L.index(3)]
              3 LOAD_FAST                0 (L)
              6 LOAD_ATTR                0 (index)
              9 LOAD_CONST               1 (3)
             12 CALL_FUNCTION            1
             15 BINARY_SUBSCR       
             16 LOAD_FAST                0 (L)  #  L[0]
             19 LOAD_CONST               2 (0)
             22 BINARY_SUBSCR       
             23 ROT_TWO             
             24 LOAD_FAST                0 (L)  # Store in L[0]
             27 LOAD_CONST               2 (0)
             30 STORE_SUBSCR        
             31 LOAD_FAST                0 (L)  # Store in L[L.index(3)]
             34 LOAD_FAST                0 (L)
             37 LOAD_ATTR                0 (index)
             40 LOAD_CONST               1 (3)
             43 CALL_FUNCTION            1
             46 STORE_SUBSCR        
             47 LOAD_CONST               0 (None)
             50 RETURN_VALUE        

格納操作は最初に格納するL[0] = 3ため、次の への呼び出しがL.index(3)返さ0れ、1したがって が元の位置に格納されます0!

以下は機能します:

L[L.index(3)], L[0] = L[0], L[L.index(3)]

L.index(3)ルックアップが最初に行われるようになったためです。ただし、.index()呼び出しの結果を一時変数に格納することをお.index()勧めします。これは、2 回呼び出しを行わない方が効率的であるためです。

于 2013-02-10T14:32:40.297 に答える
7

問題は、この 2 つが同等ではないことです。最初の例は、次のようなものです。

>>> L = [1,2,3,4,5,6,7,8,9]
>>> A = [3]
>>> i = L.index(A[0])
>>> L[0] = L[i]
>>> i = L.index(A[0])
>>> L[i] = L[0]

これは、スワップしてから、スワップしたばかりの要素を見つけて元に戻すことを意味します。

あなたが混乱している理由は、タプルの割り当てを Python が両方のことを同時に行っていると考えているからです。これは実行の実行方法ではなく、順番に実行され、結果が変わります。

たとえそれが機能したとしても、これは次善の方法であることに注意してください。list.index()特に高速な操作ではないため、理由もなく 2 回実行することはお勧めできません。

于 2013-02-10T14:24:13.940 に答える