listは、項目が_順序_どおりに並んだ可変のコレクションです。多くのコレクション(組み込みのtuple、dict、setを参照してください)と同様に、配列は、他の配列を含む、あらゆる(複数の)データ型への参照を保持できます。あらゆるシーケンス型と同様に、項目には左からは0-based indexの番号で、右からは-1-based indexの番号でアクセスできます。配列は、スライス記法や<list>.copy()を使って、全体または一部をコピーできます。
配列は、min()/max()、<list>.index()、.append()、.reverse()のような共通のシーケンス操作と可変シーケンス操作の両方をサポートします。配列の要素は、for item in <list>という構文で繰り返し処理できます。要素のインデックスと要素の値の両方が必要なときは、for index, item in enumerate(<list>)を使えます。
配列は動的配列として実装されており、JavaのArraylist型に似ています。主に、長さが不明な(項目数は自由に増えたり減ったりします)似たデータ(文字列、数値、集合など)のまとまりを格納するために使われます。
要素へのアクセス、inによるメンバーシップの確認、配列の「右側」への項目の追加は、どれもとても効率的です。配列の先頭への追加(「左側」への追加)や途中への挿入は、はるかに_非効率_です。こうした操作では、順序を保つために要素をずらす必要があるからです。両端からのメモリ効率のよいappends/popsをサポートする同様のデータ構造については、collections.dequeを参照してください。どちらの方向でもほぼ同じO(1)の性能を持ちます。
配列は可変で、任意のPythonオブジェクトへの参照を保持できるため、見かけ上の長さが同じであれば、array.arrayや(不変である)tupleよりも多くのメモリを消費します。それでも、配列は非常に柔軟で便利なデータ構造であり、Pythonの多くの組み込みメソッドや演算は、その出力として配列を生成します。
配列は、角括弧([])と要素の間のカンマを使って、_リテラル_として宣言できます:
>>> no_elements = []
>>> no_elements
[]
>>> one_element = ["Guava"]
>>> one_element
['Guava']
>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]
>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]
読みやすくするために、要素が多い場合や配列の中に入れ子のデータ構造がある場合は、改行を使えます:
>>> lots_of_entries = [
... "Rose",
... "Sunflower",
... "Poppy",
... "Pansy",
... "Tulip",
... "Fuchsia",
... "Cyclamen",
... "Lavender"
... ]
>>> lots_of_entries
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']
# Each data structure is on its own line to help clarify what they are.
>>> nested_data_structures = [
... {"fish": "gold", "monkey": "brown", "parrot": "grey"},
... ("fish", "mammal", "bird"),
... ['water', 'jungle', 'sky']
... ]
>>> nested_data_structures
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]
list()コンストラクターは、引数なしで使うことも、_イテラブル_を引数として渡して使うこともできます。イテラブルの要素はコンストラクターによって順に取り出され、順番に配列へ追加されます:
>>> no_elements = list()
>>> no_elements
[]
# The tuple is unpacked and each element is added.
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))
>>> multiple_elements_from_tuple
['Parrot', 'Bird', 334782]
# The set is unpacked and each element is added.
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})
>>> multiple_elements_from_set
[2, 3, 5, 7, 11]
文字列や辞書に対して配列のコンストラクターを使うと、意外な結果になることがあります:
# String elements (Unicode code points) are iterated through and added *individually*.
>>> multiple_elements_string = list("Timbuktu")
>>> multiple_elements_string
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']
# Unicode separators and positioning code points are also added *individually*.
>>> multiple_code_points_string = list('अभ्यास')
>>> multiple_code_points_string
['अ', 'भ', '्', 'य', 'ा', 'स']
# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
>>> source_data = {"fish": "gold", "monkey": "brown"}
>>> list(source_data)
['fish', 'monkey']
list()コンストラクターはイテラブル(または何もなし)しか引数に取れないため、イテラブルでないオブジェクトを渡すとTypeErrorが発生します。そのため、要素が1つの配列を作るには、リテラルを使うほうがずっと簡単です。
# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
>>> one_element = list(16)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
>>> one_element_from_iterable = list((16,))
>>> one_element_from_iterable
[16]
配列の中の項目(strやtupleなど、他のシーケンス型の要素も同様です)には、括弧記法_を使ってアクセスできます。インデックスは、**leftからrightへ(0から始まります)、またはrightからleft**へ(-1から始まります_)進みます。
| 左からのインデックス⟹ |
|
⟸右からのインデックス |
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]
# Oatmeal is at index 0 or index -4.
>>> breakfast_foods[0]
'Oatmeal'
>>> breakfast_foods[-4]
'Oatmeal'
# Eggs are at index -2 or 2
>>> breakfast_foods[-2]
'Eggs'
>>> breakfast_foods[2]
'Eggs'
# Toast is at -1
>>> breakfast_foods[-1]
'Toast'
配列の一部には、スライス記法(<list>[<start>:<stop>])でアクセスできます。_スライス_は、start <= index < stopとなる位置indexにある要素の並びと定義されます。スライスは「切り出した」項目のコピーを返し、元のlistは変更しません。
スライスではstepパラメーターも使え(<list>[<start>:<stop>:<step>])、返される要素を「飛ばし飛ばし」にしたり絞り込んだりできます(たとえば、stepに2を指定すると、その範囲の1つおきの要素が選ばれます):
>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]
# If there is no step parameter, the step is assumed to be 1.
>>> middle_colors = colors[2:6]
>>> middle_colors
['Green', 'Yellow', 'Orange', 'Pink']
# If the start or stop parameters are omitted, the slice will
# start at index zero, and will stop at the end of the list.
>>> primary_colors = colors[::3]
>>> primary_colors
['Red', 'Yellow', 'Blue']
配列はイテレーターを提供し、他の_シーケンス型_と同じように、for item in <list>またはfor index, item in enumerate(<list>)を使って繰り返し処理できます:
# Make a list, and then loop through it to print out the elements
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for item in colors:
... print(item)
Orange
Green
Grey
Blue
# Print the same list, but with the indexes of the colors included
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for index, item in enumerate(colors):
... print(item, ":", index)
Orange : 0
Green : 1
Grey : 2
Blue : 3
# Start with a list of numbers and then loop through and print out their cubes.
>>> numbers_to_cube = [5, 13, 12, 16]
>>> for number in numbers_to_cube:
... print(number**3)
125
2197
1728
4096
値の配列を作る一般的な方法の1つは、ループの中で<list>.append()を使うことです:
>>> cubes_to_1000 = []
>>> for number in range(11):
... cubes_to_1000.append(number**3)
>>> cubes_to_1000
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
配列は、さまざまな方法で組み合わせることもできます:
# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]
>>> new_via_concatenate
['George', 5, 'cat', 'Tabby']
# Likewise, using the multiplication operator * is the equivalent of using + n times.
>>> first_group = ["cat", "dog", "elephant"]
>>> multiplied_group = first_group * 3
>>> multiplied_group
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']
# Another method for combining 2 lists is to use slice assignment or a loop-append.
# This assigns the second list to index 0 in the first list.
>>> first_one = ["cat", "Tabby"]
>>> second_one = ["George", 5]
>>> first_one[0:0] = second_one
>>> first_one
['George', 5, 'cat', 'Tabby']
# This loops through the first list and appends its items to the end of the second list.
>>> first_one = ["cat", "Tabby"]
>>> second_one = ["George", 5]
>>> for item in first_one:
... second_one.append(item)
>>> second_one
['George', 5, 'cat', 'Tabby']
Pythonの変数は、_背後にあるオブジェクト_を指す_ラベル_であることを思い出してください。listsは、_コンテナオブジェクト_としてもう1つ層を加えます。コンテナオブジェクトは、集めた項目のオブジェクト_参照_を保持します。これを適切に扱わないと、配列を扱うときにさまざまな問題が起こり得ます。
listオブジェクトを新しい変数_名_に代入しても、listオブジェクトもその要素もコピーされません。_新しい_名前でそのlistの要素を変更すると、元のlistにも影響します。
list.copy()やスライスでshallow_copyを作ると、この第1層の参照の問題を避けられます。shallow_copyは新しいlistオブジェクトを作りますが、含まれているlistの_要素_の新しいオブジェクトは作りません。この種のコピーは通常、2つのlistオブジェクトに対して独立に項目を追加・削除するのに十分で、事実上2つの「別々の」配列を持つことになります。
>>> actual_names = ["Tony", "Natasha", "Thor", "Bruce"]
# Assigning a new variable name does not make a copy of the container or its data.
>>> same_list = actual_names
# Altering the list via the new name is the same as altering the list via the old name.
>>> same_list.append("Clarke")
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
>>> actual_names
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
# Likewise, altering the data in the list via the original name will also alter the data under the new name.
>>> actual_names[0] = "Wanda"
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']
# If you copy the list, there will be two separate list objects which can be changed independently.
>>> copied_list = actual_names.copy()
>>> copied_list[0] = "Tony"
>>> actual_names
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']
>>> copied_list
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
この参照の問題は、入れ子になった配列や掛け算で作った配列を扱うときにさらに深刻になります(以下の例は、2013年の優れたNed Batchelderのブログ記事Names and values: making a game boardからのものです):
from pprint import pprint
# This will produce a game grid that is 8x8, pre-populated with zeros.
>>> game_grid = [[0]*8]*8
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
# An attempt to put a "X" in the bottom right corner.
>>> game_grid[7][7] = "X"
# This attempt doesn't work because all the rows are referencing the same underlying list object.
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X'],
[0, 0, 0, 0, 0, 0, 0, 'X']]
しかしこの状況では、shallow_copyで望む動作を実現できます:
from pprint import pprint
# This loop will safely produce a game grid that is 8x8, pre-populated with zeros
>>> game_grid = []
>>> filled_row = [0] * 8
>>> for row in range(8):
... game_grid.append(filled_row.copy()) # This is making a new shallow copy of the inner list object each iteration.
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
# An attempt to put a "X" in the bottom right corner.
>>> game_grid[7][7] = "X"
# The game grid now works the way we expect it to!
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 'X']]
先ほど触れたように、配列は_参照_のコンテナなので、2つ目の層の問題もあり得ます。配列に変数、オブジェクト、入れ子になったデータ構造が含まれている場合、それらの第2層の参照は、shallow_copyやスライスではコピーされません。そのため、背後にあるオブジェクトを変更すると、_すべての_コピーに影響します。それぞれのlistオブジェクトは、含まれている要素を_指す参照_しか持っていないからです。
from pprint import pprint
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 'X']]
# We'd like a new board, so we make a shallow copy.
>>> new_game_grid = game_grid.copy()
# But a shallow copy doesn't copy the contained references or objects.
>>> new_game_grid[0][0] = 'X'
# So changing the items in the copy also changes the originals items.
>>> pprint(game_grid)
[['X', 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 'X']]
配列は_スタック_や_キュー_としてよく使われますが、その実装上、先頭への追加や途中への挿入は遅くなります。collectionsモジュールには、両端からの高速な追加と取り出しに最適化されたdequeがあり、これは双方向連結リストとして実装されています。入れ子の配列は小さな_行列_を表すのにも使われますが、効率的な行列や表形式データの操作には、NumpyやPandasライブラリのほうがはるかに強力です。collectionsモジュールには、専門的な配列の用途に合わせてカスタマイズできるUserList型もあります。