web-dev-qa-db-ja.com

タプルの解凍順序により、割り当てられた値が変更されます

2つは同じだと思います。

nums = [1, 2, 0]    
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]    
print nums  # [2, 1, 0]

nums = [1, 2, 0]    
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]    
print nums  # [2, 2, 1] 

しかし、結果は異なります。
結果が異なるのはなぜですか? (なぜ2番目の結果になるのですか?)

48
henry

前提条件-2つの重要なポイント


はじめに

a,b = c,dを実行すると、cdの値が最初に保存されます。次に、左側から始めて、aの値を最初にcに変更し、次にbの値をdに変更します。

ここでの落とし穴は、bの値を変更しているときにaの場所に副作用がある場合、d後でb、これはbの副作用の影響を受けるaです。


ユースケース

今あなたの問題に来ています

最初のケースでは、

nums = [1, 2, 0]    
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]    

nums[0]は最初は1であり、nums[nums[0]]2と評価されるため、nums[1]です。したがって、1,2はメモリに保存されます。

タプルの解凍は左側から行われるようになりました。

nums[nums[0]] = nums[1] = 1   # NO side Effect. 
nums[0] = 2

したがって、print nums[2, 1, 0]を出力します

ただし、この場合

nums = [1, 2, 0]   
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]    

nums[nums[0]], nums[0]は、最初の場合と同じように、2,1をスタックに置きます。

ただし、左側、つまりnums[0], nums[nums[0]]では、nums[0]の変更は、nums[nums[0]]のインデックスとして使用されるため、副作用があります。したがって、

nums[0] = 2
nums[nums[0]] = nums[2] = 1  # NOTE THAT nums[0] HAS CHANGED

nums[1]は値2で変更されません。したがって、print nums[2, 2, 1]を出力します

46
Bhargav Rao

プロセスを追跡するクラスを定義できます。

class MyList(list):
    def __getitem__(self, key):
        print('get ' + str(key))
        return super(MyList, self).__getitem__(key)
    def __setitem__(self, key, value):
        print('set ' + str(key) + ', ' + str(value))
        return super(MyList, self).__setitem__(key, value)

最初の方法の場合:

nums = MyList([1, 2, 0])
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]

出力は次のとおりです。

get 0
get 0
get 1
get 0
set 1, 1
set 0, 2

2番目の方法:

nums = MyList([1, 2, 0])
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]

出力は次のとおりです。

get 0
get 1
get 0
set 0, 2
get 0
set 2, 1

どちらの方法でも、最初の3行はタプルの生成に関連し、最後の3行は割り当てに関連しています。最初のメソッドの右側のタプルは(1, 2)で、2番目のメソッドは(2, 1)です。

割り当て段階では、最初のメソッドはnums[0]である1を取得し、nums[1] = 1を設定し、次にnums[0] = 2を設定し、2番目のメソッドはnums[0] = 2を割り当て、次にnums[0]である2を取得し、最後にnums[2] = 1を設定します。

18
eph

そのため、python割り当ての優先度は左から右になります。したがって、次のコードでは次のようになります。

 nums = [1, 2, 0]
 nums[nums[0]], nums[0] = nums[0], nums[nums[0]]

最初にnums[0]nums[nums[0]]に割り当てました。これはnums[1]==1を意味し、リストは可変オブジェクトであるため、数値は次のようになります。

[1,1,0]

次に、nums[nums[0]]nums[0]に割り当てられます。これは、nums[0]==2と:を意味します。

nums = [2,1,0]

そして第二部もそうです。

ここで重要な点は、リストオブジェクトは変更可能であり、コードのセグメントで変更すると、その場で変更できることに注意してください。したがって、残りのコードに影響します。

評価順

Pythonは式を左から右に評価します。割り当てを評価するとき、右側が左側の前に評価されることに注意してください。

10
Kasrâmvd

最初の例では、予想どおり、nums [1]が1に設定され、次にnums [0]が2に設定されます。

2番目の例では、nums [0]が2に設定され、次にnums [2]が1に設定されます。これは、この場合、左側のnums [nums [0] ]は、割り当てが発生したときに実際にnums [2]を参照しています。これは、nums [0]が2に設定されたばかりだからです。