14

Pythonを学習しようとしたときに、この問題に遭遇しました。次の関数を検討してください。

def swap0(s1, s2):
    assert type(s1) == list and type(s2) == list
    tmp = s1[:]
    s1 = s2[:]
    s2 = tmp
return

s1 = [1]
s2 = [2]
swap0(s1, s2)
print s1, s2

s1 と s2 は何を出力しますか?

問題を実行した後、print ステートメントが 1 2 を出力することがわかりました。s1 と s2 の値はswap0関数から変更されていないようです。私が考えることができた唯一の説明は、線のためでした.

tmp = s1[:]

s1[:] はコピーであるため、関数呼び出しで s1 の値が変更されないことは理にかなっています。ただし、のパラメーターswap0は (s1, s2) であるため、tmp = s1[:] を実行した後かどうかはわかりません。いつでも

s1 = something...

s1 自体ではなく、s1 のコピーへの参照になります。誰かがより良い説明を提供できますか? ありがとう。

4

8 に答える 8

31

これは、関数内s1および関数s2内に新しい値を割り当てるためswap0です。これらの代入は、関数の外部には伝播しません。関数呼び出しの場所に関数本体をコピーして貼り付けるだけで機能することがわかります。

引数自体ではなく、引数によって参照されるオブジェクトを変更することで、これを回避できます。

def swap0(s1, s2):
    assert type(s1) == list and type(s2) == list
    tmp = s1[:]
    s1[:] = s2
    s2[:] = tmp

ただし、Python でスワップを行うためのより簡単で優れた方法は、次のとおりです。

s1, s2 = s2, s1

これも、リストへの特定の参照のみを交換しますが、リストの内容自体は交換しません。

于 2012-10-31T21:00:25.010 に答える
13

そのままで、 finalprintは と の元の値を出力s1s2ます。これは、関数のスコープ内でのみ交換しているためです。そうしても、関数の外側の値には影響しません (つまり、関数が呼び出された後の値の後)。

それらが変更可能な型 ( listsetdictなど) である場合は、 内でインプレースで変更できますswapswapただし、それは変更可能な型でのみ機能するように制限されています。

したがって、入力を逆の順序で返すことをお勧めします。

def swap(s1, s2):
    return s2, s1

s1 = 'a'
s2 = 'b'
s1, s2 = swap(s1, s2)
print s1, s2 # prints 'b a'

もちろん、次のようにこれをすべて 1 行で行うこともできます。

s1, s2 = s2, s1

乾杯!

于 2012-10-31T21:02:09.353 に答える
6

他の答えは、何がうまくいかないかを説明しています。これがあなたが望むことをするバージョンです:

def swap(s1, s2):
    assert isinstance(s1, list) and isinstance(s2, list)
    s1[:], s2[:] = s2[:], s1[:]

参照: isinstance と type

于 2012-10-31T21:09:29.230 に答える
4

関数内で、ローカル変数s1を再バインドs2し、右側の値を使用します (スライスを使用してコピーを作成しているため、これもローカルです)。これらのローカル変数の内容を変更しても、同じリストを参照しなくなるため、呼び出しスコープ内のリストの内容は変更されません。

于 2012-10-31T21:00:33.697 に答える