Pythonリストスライスで割り当てはどのように機能しますか?


99

Python docは、リストをスライスすると新しいリストが返されると言っています。
「新しい」リストが返される場合、「スライスへの割り当て」に関連する次の疑問があります。

a = [1, 2, 3]
a[0:2] = [4, 5]
print a

出力は次のようになります。

[4, 5, 3] 
  1. 何かを返す何かが表現の左側に来ることができるでしょうか?
  2. はい、私はドキュメントを読んでそれが可能であると述べていますが、リストをスライスすると「新しい」リストが返されるので、元のリストが変更されているのはなぜですか?その背後にあるメカニズムを理解できません。

@マークLongair申し訳ありませんが、私は思っただけでコードが出力しないフォーマットされたことになっている
Kartikアナンドに


7
あなたは何a[0] = 4をするか理解していますか?
ジョシュリー

1
@KartikAnandスライスの割り当ては、新しいリストが作成されない特別なシナリオです。の左側に名前バインディングがないオブジェクトを作成するのは意味がありません=。そのため、これを無効な構文として破棄する代わりに、Pythonはそれを予想以上のものに変換します。pythonには参照がないため、スライスの結果で元のリストを変更しても機能しません。あなたはコピーを取得します。アプリケーションについてより多くの情報を提供していただければ、 'pythonic'の方法でより適切にサポートできる可能性があります。:)
Casey Kuball

1
@Darthfett私は現在、どのアプリケーションにも取り組んでいません。手を汚す前に、自分自身にPythonを教えています:)
Kartik Anand

回答:


114

非常に似た構文を使用する2つの異なる操作を混乱させています。

1)スライス:

b = a[0:2]

これにより、のスライスのコピーが作成されa、に割り当てられbます。

2)スライスの割り当て:

a[0:2] = b

これ、のスライスaをの内容に置き換えますb

構文は似ていますが(設計上想像します!)、これらは2つの異なる操作です。


4
それが私の疑問です。2番目のケースでは、なぜ新しいリストのスライスではないのですか?
カルティックアナンド

11
@KartikAnandないので。それは言語が指定するものではありません。
Marcin

明確に言うと、「スライスをとる」とは、「スライスのコピーを作成する」という意味であり、混乱の原因となっています。
Mark Ransom

2
@KartikAnand:基本的にはい。インタープリターはどちらがどれかを認識し、適切に処理します。
NPE

1
@Dubslow:itertoolsモジュールを使用してそれを行うことができます。あなたのケースでは、関数islice、with start=1、を使用してくださいstop=None。これにより、コピーが回避され、遅延評価が使用されます(あなたの場合、元のリストへの遅延アクセス)。
スピロス2015年

66

演算子のa左側で指定する場合=、Pythonの通常の割り当てを使用しています。これによりa、現在のコンテキストの名前が新しい値を指すように変更されます。これは、ポイントしていた以前の値を変更しませんa

演算子のa[0:2]左側を指定することで、スライス割り当て=を使用することをPythonに指示しています。スライス割り当ては、リストのコンテンツを挿入、削除、または置換できるリストの特別な構文です。

挿入

>>> a = [1, 2, 3]
>>> a[0:0] = [-3, -2, -1, 0]
>>> a
[-3, -2, -1, 0, 1, 2, 3]

削除

>>> a
[-3, -2, -1, 0, 1, 2, 3]
>>> a[2:4] = []
>>> a
[-3, -2, 1, 2, 3]

交換

>>> a
[-3, -2, 1, 2, 3]
>>> a[:] = [1, 2, 3]
>>> a
[1, 2, 3]

注意:

スライスの長さは、割り当てられたシーケンスの長さとは異なる場合があります。そのため、ターゲットシーケンスで可能であれば、ターゲットシーケンスの長さを変更します。- ソース

スライスの割り当ては、タプルの解凍と同様の機能を提供します。たとえば、次a[0:1] = [4, 5]と同等です。

# Tuple Unpacking
a[0], a[1] = [4, 5]

タプルのアンパックを使用すると、非順次リストを変更できます。

>>> a
[4, 5, 3]
>>> a[-1], a[0] = [7, 3]
>>> a
[3, 5, 7]

ただし、要素を挿入または削除できないため、タプルのアンパックは置換に限定されます。

これらすべての操作の前と後aは、まったく同じリストです。Pythonは、リストをインプレースで変更するための優れた構文シュガーを提供するだけです。


6
左と右の要素の数が等しくない可能性があるため、似ていますが同一ではありません。
Mark Ransom

@MarkRansomこれはすばらしい点です。これを明確にするために、さらに情報を追加しました。
Casey Kuball

2
であるa[:] = some_listと同等a = some_list[:]a = some_list
jadkik94

2
@ jadkik94どちらでもない。 a[:] = some_listのすべての要素をの要素に設定aしますsome_list。あなたが言及したもののどちらかを行うと、何aが変わるでしょう。次に例を示しますa = [1, 2, 3] b = a a[:] = [4, 5, 6] a is b。最後の行は、そのa値を変更するのではなく、変更した場合はFalseになります。
ケイシークボール

@Darthfett興味深い、私は別の方法で見つけた:)ありがとう。
jadkik94

25

私は以前に同じ質問に出くわしました、そしてそれは言語仕様に関連しています。割り当てステートメントによれば、

  1. 割り当ての左側がサブスクリプションの場合、Pythonは__setitem__そのオブジェクトを呼び出します。a[i] = xと同等a.__setitem__(i, x)です。

  2. 割り当ての左側がスライスの場合、Pythonもを呼び出します__setitem__が、引数a[1:4]=[1,2,3]は 異なります。 a.__setitem__(slice(1,4,None), [1,2,3])

そのため、「=」の左側にあるリストスライスの動作は異なります。


4

割り当て操作の左側をスライスすると、割り当てるアイテムを指定できます。

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