Pythonで2つの変数を交換する標準化された方法はありますか?


345

Pythonでは、次の構文を使用して2つの変数値が交換されるのを見ました。

left, right = right, left

これは2つの変数値を交換する標準的な方法と考えられていますか、それとも2つの変数が慣例的に最も普通に交換される他の方法がありますか?


1
@eyquem:評価順序がタプル/リスト割り当ての言語によって定義されているかどうかにかかっています。Pythonではサポートされていますが、ほとんどの古い言語ではサポートされていません。
smci

Hrmm C ++にはswap(a [i]、a [k])があるので、Pythonにこのようなものはありません。
Nils

回答:


389

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

http://docs.python.org/3/reference/expressions.html#evaluation-order

これは、式の次のことを意味しますa,b = b,a

  • 右側b,aが評価されます。つまり、2つの要素のタプルがメモリに作成されます。2つの要素は、プログラムの実行中に命令が検出される前に存在していた、識別子baで指定されたオブジェクトです。
  • このタプルの作成直後は、このタプルオブジェクトの割り当てはまだ行われていませんが、重要ではありません。Pythonは内部的にどこにあるかを認識しています
  • 次に、左側が評価されます。つまり、タプルが左側に割り当てられます
  • 左側は、二つの識別子で構成されているように、タプルは、第一の識別子がそのために解凍されaたタプルの最初の要素に割り当てられる(formelyたオブジェクトであるbは、それが名前を持っていたため、スワップ前b
    と2番目の識別子bは、タプルの2番目の要素に割り当てられます(これは、識別子がだったため以前はスワップ前にあったオブジェクトですa

このメカニズムは効果的識別子に割り当てられたオブジェクトをスワップしているaと、b

したがって、あなたの質問に答えるには:はい、これは2つのオブジェクトで2つの識別子を交換する標準的な方法です。
ちなみに、オブジェクトは変数ではなく、オブジェクトです。


1
私が理解している限り、この方法で2つの変数を交換しても、余分なメモリは使用されず、2つの変数自体のメモリだけが使用されますか?
Catbuilt

1
@Catbuiltsタプルを構築すると、余分なメモリが必要になります(スワップの3変数バージョンが占める可能性があるよりも高い可能性があります)が、スワップされるのはメモリアドレスだけなので、絶対的なメモリはそれほど多くありませんセンス(多分24バイト余分)。
ブリリアント、

@ブリリアン:ありがとう。この主題に関する文書はありますか?それは非常に興味深く、私はさらに読んでもらいたいと思います。ありがとう。
Catbuilt

1
@Catbuiltsよくわかりませんが、C ++ポインターのしくみを確認すると役立つ場合があります。Pythonは自動的に最善の方法で物事を実行しようとしますが、C ++は実際にすべての可能なオプションを提供して、Pythonが採用するアプローチの欠点を理解するための良い出発点となります。 。また、「64ビット」のオペレーティングシステムを使用すると、メモリアドレスを保存すると64ビットのメモリが消費されます。これは、「24バイト」の数値を取得した場所の一部です。
Brilliand

素晴らしい説明。これを追加するだけで、このメソッドを使用して任意の数の「変数」を再配置できますa, b, c = c, a, b
alexlomba87


38

a, b = b, aは変数を交換する3つの方法を知っていますが、最も簡単です。有る

XOR(整数用)

x = x ^ y
y = y ^ x
x = x ^ y

または簡潔に、

x ^= y
y ^= x
x ^= y

一時変数

w = x
x = y
y = w
del w

タプルスワップ

x, y = y, x

1
難読化されていない最も単純で唯一のものです。
Jorge Leitao

17
XORは「変数」を交換しません。整数変数を交換します。(またはXOR演算子を適切に実装している他のいくつかのタイプ)さらに、Rogalskiの回答によると、タプルスワップはインタープリターで最適化されているため、実際には何も影響を受けません。短く、明確で、速い。
Rawler

XORの問題は+-演算子を使用することで回避できますが、それでも、a、b = b、a codex = x + yy = xy x = xycode
ashish

22

予期しないエラーが発生するため、スワップの標準的な方法であるとは言えません。

nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]

nums[i]最初に変更され、次に2番目の変数に影響しますnums[nums[i] - 1]


2
aがbに依存している場合、またはその逆の場合、swap(a、b)を使用するのは安全ではありません。例えば、スワップ(b)は、に拡張されるかもしれません:var c=aa=bb=c。そして、最後の割り当てでは、新しい値を使用してabのアドレスを評価します。
Kai Petzke

1
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]。問題を解決します。
ビル・チェン

1
これは問題を解決しますが、なぜですか?
ジャクソンケリー

2
@JacksonKelley右側の評価は安全です。In nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]:問題は、Pythonが左側の代入を行うときnums[i]に変更され、nums[nums[i] - 1]予期しない変更が発生することです。あなたは最初にあなたnums[1],nums[2] = nums[2],nums[1]が望むことを想像することができ nums[1] = nums[2]ますが、実行後、あなたはもう持っていませんnums[2] = nums[1]、代わりにあなたは得ましたnums[888] = nums[1]
guo

5

ここでは参照が使用されるため、多次元配列では機能しません。

import numpy as np

# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)

# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)

Numpy配列のスライスの交換も参照してください


これは確かにnumpyライブラリの特別な機能(またはバグ)です。
Kai Petzke

-1

eyquemで説明されている問題を回避するには、copyモジュールを使用して、関数を介して、値の(逆の)コピーを含むタプルを返すことができます。

from copy import copy

def swapper(x, y):
  return (copy(y), copy(x))

と同じ機能lambda

swapper = lambda x, y: (copy(y), copy(x))

次に、次のように、それらを目的の名前に割り当てます。

x, y = swapper(y, x)

注:必要に応じて、のdeepcopy代わりにインポート/使用できますcopy


コピーで解決しようとしている問題は何ですか?
Hanan Shteingart

eyquemの投稿で議論されたもの。
LogicalBranch

1
しかし、それは彼が「YES、それは二つの識別子スワップへの標準的な方法だ」と言い、実際には、任意の問題を述べていません
ハナンShteingart

-2

タプルXORスワップを組み合わせることができます:x、y = x ^ x ^ y、x ^ y ^ y

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

x, y = x ^ x ^ y, x ^ y ^ y

print('After swapping: x = %s, y = %s '%(x,y))

または

x, y = 10, 20

print('Before swapping: x = %s, y = %s '%(x,y))

print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))

ラムダを使用する:

x, y = 10, 20

print('Before swapping: x = %s, y = %s' % (x, y))

swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))

print('After swapping: x = %s, y = %s ' % swapper(x, y))

出力:

Before swapping: x =  10 , y =  20
After swapping: x =  20 , y =  10
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.