listは、要素を_順序_どおりに並べた、変更可能なコレクションです。ほとんどのコレクション(組み込みのtuple、dict、setなど)と同様に、配列は任意のデータ型(複数のデータ型でも可)への参照を保持できます。ほかの配列への参照も含まれます。Pythonではシーケンス型とみなされ、スライス記法を使えば全体も一部もコピーできます。ほかのシーケンスと同じく、lists内の要素は、左からは0-based index番号、右からは-1-based index番号で参照できます。
配列は、共通のシーケンス操作と変更可能なシーケンス操作の両方をサポートしています。たとえば、min(<list>)/max(<list>)、<list>.index()、<list>.append()、<list>.reverse()などです。for item in <list>という構文を使えば要素を繰り返し処理できます。要素とそのインデックスの両方が必要なときは、for index, item in enumerate(<list>)を使います。
Pythonには、配列を扱うための便利なメソッドがたくさん用意されています。配列は変更可能なので、配列のメソッドは、そのメソッドに渡された元の配列オブジェクト自体を変更します。変更したくない場合は、sliceまたは<list>.copy()を使って、元のlistのshallow copy(少なくとも)を作る必要があります。
既存の配列の末尾に要素を追加するには、<list>.append(<item>)を使います。
>>> numbers = [1, 2, 3]
>>> numbers.append(9)
>>> numbers
[1, 2, 3, 9]
_追加_ではなく、<list>.insert(<index>, <item>)を使うと、配列内の_特定のインデックス_に要素を追加できます。<index>は、新しい要素をその_直前に_入れたい要素のインデックスです。<item>は挿入する要素です。
注意:<index>が0の場合、要素は配列の先頭に追加されます。<index>が配列の最後のインデックスより大きい場合、要素は末尾に追加されます。これは<list>.append(<item>)を使った場合と同じです。
>>> numbers = [1, 2, 3]
>>> numbers.insert(0, -2)
>>> numbers
[-2, 1, 2, 3]
>>> numbers.insert(1, 0)
>>> numbers
[-2, 0, 1, 2, 3]
<list>.extend(<iterable>)を使うと、iterableを既存の配列と_連結_できます(2つをつなげる)。<list>.extend(<iterable>)は、渡されたイテラブルを_展開_し、その要素を同じ順序で対象の配列の末尾に追加します。この場面で<list>.append(<item>)を使うと、イテラブル全体が_1つの要素_として追加されてしまいます。
>>> numbers = [1, 2, 3]
>>> other_numbers = [5, 6, 7]
>>> numbers.extend(other_numbers)
>>> numbers
[1, 2, 3, 5, 6, 7]
>>> numbers.extend([8, 9])
>>> numbers
[1, 2, 3, 5, 6, 7, 8, 9]
<list>.remove(<item>)を使うと、配列から要素を削除できます。配列にその要素が存在しない場合、<list>.remove(<item>)はValueErrorを投げます。
>>> numbers = [1, 2, 3]
>>> numbers.remove(2)
>>> numbers
[1, 3]
# Trying to remove a value that is not in the list throws a ValueError.
>>> numbers.remove(0)
ValueError: list.remove(x): x not in list
別の方法として、<list>.pop(<index>)メソッドを使うと、要素を削除すると同時に、その要素をreturnで返して利用できます。
<list>.pop(<index>)は、省略可能な仮引数を1つ取ります。削除して返す要素のindexです。省略可能な<index>引数を指定しない場合は、配列の最後の要素が削除されて返されます。<index>が配列の最後のインデックスより大きい数値の場合、IndexErrorが投げられます。
>>> numbers = [1, 2, 3]
>>> numbers.pop(0)
1
>>> numbers
[2, 3]
>>> numbers.pop()
3
>>> numbers
[2]
# This will throw an error because there is only index 0.
>>> numbers.pop(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop index out of range
<list>.clear()を使うと、listからすべての要素を削除できます。引数は取りません。
>>> numbers = [1, 2, 3]
>>> numbers.clear()
>>> numbers
[]
配列の要素の順序は、<list>.reverse()を使うと_その場で_反転できます。これは元の配列を変更します。
>>> numbers = [1, 2, 3]
>>> numbers.reverse()
>>> numbers
[3, 2, 1]
配列は、<list>.sort()を使うと_その場で_並べ替えられます。既定の並べ替え順は、左から_昇順_です。Pythonのドキュメントには並べ替えの追加のヒントとテクニックが載っています。
2002年から2022年まで、Pythonは内部的にTimsortというアルゴリズムを使って配列を並べ替えていましたが、Python 3.11以降はPowersortに切り替えました。この変更についての詳細と、Pythonコアチームによる議論は、GitHubのissue 78742で読むことができます。
アルゴリズムの技術的な詳細については、J. Ian MunroとSebastian Wildによる論文Nearly-Optimal Mergesorts: Fast, Practical Sorting Methods That Optimally Adapt to Existing Runsをご覧ください。
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
# The default sort order is *ascending*.
>>> names.sort()
>>> names
["Bruce", "Natasha", "Thor", "Tony"]
配列を_降順_に並べ替えるには、メソッドにreverse=Trueという引数を渡します。
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
>>> names.sort(reverse=True)
>>> names
["Tony", "Thor", "Natasha", "Bruce"]
元のlistを変更したくない場合は、組み込み関数のsorted()とreversed()を使えます。sorted(<list>)は並べ替えた_コピー_を返し、<list>.sort()と同じ仮引数を取ります。reversed(<list>)は、配列の要素を逆順に生成する_イテレーター_を返します。Iteratorsについては、別の演習で詳しく扱います。
配列内のある要素が何回出現するかを調べるには、<list>.count(<item>)を使います。配列内に<item>が要素として現れる回数の合計を返します。
>>> items = [1, 4, 7, 8, 2, 9, 2, 1, 1, 0, 4, 3]
>>> items.count(1)
3
<list>.index(<item>)は、渡された要素が_最初に出現する_位置のインデックス番号を返します。出現しない場合は、ValueErrorが発生します。インデックスは左から0-basedで、最初の要素の位置はインデックス0です。右からのインデックス付けもサポートされており、-1から始まります。
>>> items = [7, 4, 1, 0, 2, 5]
>>> items.index(4)
1
>>> items.index(10)
ValueError: 10 is not in list
startとendのインデックスを指定すると、配列の特定の範囲内を検索します。
>>> names = ["Tina", "Leo", "Thomas", "Tina", "Emily", "Justin"]
>>> names.index("Tina")
0
>>> names.index("Tina", 2, 5)
3
要素の正確な位置が必要ない場合は、組み込みのin演算子を使ったほうが、要素が含まれているかを効率よく確認できます。
>>> names = ["Tina", "Leo", "Thomas", "Tina", "Emily", "Justin"]
>>> "Thomas" in names
True
Pythonの変数は、背後にあるオブジェクトを指す名前であることを覚えておきましょう。名前は、プログラムの実行中に別のオブジェクトに束縛したり、束縛し直したりできます。listオブジェクトを新しい変数_名_に代入しても、そのオブジェクトや参照しているデータはコピーされません。新しい名前も古い名前も、同じlistオブジェクトを指します。さらに、配列は_コンテナ_型のオブジェクトです。領域を節約するため、コンテナはメンバーの要素そのものではなく、要素への_参照_だけを保持します。この「最適化」は、油断していると思わぬ結果を招くことがあります。
<list>.copy()は新しいlistオブジェクトを作成しますが、参照されている配列の_要素_に対して新しいオブジェクトを作成することはありません。このコピーは「浅い」コピーです。一方のlistオブジェクトに要素を追加したり削除したりしても、もう一方を変更したくない場合には、shallow copyで通常は十分です。ただし、listの_背後の要素_が誤って変更される可能性が少しでもある場合(その結果、関連する浅いコピーがすべて変更されます)は、copyモジュールのcopy.deepcopy()を使って、すべての参照とオブジェクトの完全な、つまり「深い」コピーを作成してください。
名前、値、配列、入れ子になった配列の動作についての詳しい説明は、Ned Batchelderによるこちらの優れたブログ記事、Names and values: making a game boardをご覧ください。Shallow vs Deep Copying of Python Objectsにも、コピーに関する考慮事項がよくまとまっています。