6. リスト#

6.1. 基本#

リストは複数の値・オブジェクトをまとめる。要素を,で区切って並べ、全体を[ ]で囲んで作成する。

a = [0, 1, 4, 9, 16, 25, 36, 49]
a
[0, 1, 4, 9, 16, 25, 36, 49]
type(a)
list

先頭要素のインデックス番号は0。

a[0]
0
a[1]
1

リストの要素数はlen関数で取得する。

len(a)
8

要素の範囲を超えてリストにアクセスするとエラーになる。

a[8]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/tmp/ipykernel_204952/767257108.py in <module>
----> 1 a[8]

IndexError: list index out of range

リストの最後の要素のインデックス番号は「要素数 - 1」。

a[len(a)-1]
49

リストの要素を末尾からアクセスするため、負のインデックス番号を用いることができる。

a[-1]
49
a[-2]
36

空のリストは[]list関数で作成する。

a = []
a
[]
a = list()
a
[]

6.2. スライス#

a[start:end:step]という形式で、リストの要素の部分列を抽出できる(「スライス」と呼ばれる)。

  • start: 抽出を開始する要素のインデックス番号。省略された場合は0となる。

  • end: 抽出を終了する要素のインデックス番号(ただし、a[end]の要素は含まれない)。省略された場合はリストの要素数となる。

  • step: 要素を抽出する間隔。省略された場合は1である。

a = [0, 1, 4, 9, 16, 25, 36, 49]

開始位置と終了位置を指定したスライス。

a[2:5]
[4, 9, 16]
a[0:3]
[0, 1, 4]

開始位置を省略したスライス。

a[:3]
[0, 1, 4]

終了位置を省略したスライス。

a[5:]
[25, 36, 49]

負のインデックス番号を用いてもよい。

a[-3:]
[25, 36, 49]

要素を取り出す間隔を指定したスライス。

a[1::2]
[1, 9, 25, 49]

間隔に-1を指定して、逆順に要素を取り出すスライス(a[5], a[4], a[3]が取り出される)。

a[5:2:-1]
[25, 16, 9]

開始位置と終了位置を省略し、全ての要素を逆順に取り出すスライス。

a[::-1]
[49, 36, 25, 16, 9, 4, 1, 0]

6.3. リストの要素に対する繰り返し#

a = [0, 1, 4, 9, 16, 25, 36, 49]

リストの要素に先頭から繰り返しアクセスする。

for x in a:
    print(x)
0
1
4
9
16
25
36
49

リストの要素に先頭から繰り返しアクセスすると同時に、各要素のインデックス番号を得る。

for i, x in enumerate(a):
    print(i, x, a[i])
0 0 0
1 1 1
2 4 4
3 9 9
4 16 16
5 25 25
6 36 36
7 49 49

リストの要素に末尾から繰り返しアクセスする。

for x in reversed(a):
    print(x)
49
36
25
16
9
4
1
0

6.4. リストの要素に対する操作#

wd = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']
wd
['monday', 'tuesday', 'wednesday', 'thursday', 'friday']

リスト内の要素を変更する。

wd[1] = 'tue'
wd
['monday', 'tue', 'wednesday', 'thursday', 'friday']

末尾に要素を追加する。

wd = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']
wd.append('saturday')
wd
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']

指定した要素を削除する。

del wd[5]
wd
['monday', 'tuesday', 'wednesday', 'thursday', 'friday']

リストを連結して、新しいリストを作成する。

d = wd + ['saturday', 'sunday']
d
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']

リストをスタックと見なしてpop操作をすると、末尾の要素が取り出され、同時に末尾の要素がリストから削除される。

d.pop()
'sunday'
d
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']

push操作に相当するものはappend関数である。

d.append('sun')
d
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sun']

6.5. 要素の並び替え#

要素を降順に並べたリストを新たに作成する(この場合は辞書順に並ぶ)。

sorted(d)
['friday', 'monday', 'saturday', 'sun', 'thursday', 'tuesday', 'wednesday']

元のリスト(d)は変更されていない。

d
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sun']

引数reverseをTrueにセットすると、要素を昇順に並べたリストを新たに作成できる(この場合は辞書の逆順に並ぶ)。

sorted(d, reverse=True)
['wednesday', 'tuesday', 'thursday', 'sun', 'saturday', 'monday', 'friday']

並び替えにおける要素の並び順は要素に対する比較演算子<の結果に基づく。例えば、'saturday' < 'sun'が成り立つので、'saturday''sun'の前に並ぶ。要素を比較するときの基準を変更したいときは、引数keyを用いる。以下の例では、要素の長さ(文字数)を取得する無名関数(lambda)を定義し、それをkey引数に渡すことで、文字数の少ない要素の順に並び替えている。

sorted(d, key=lambda x: len(x))
['sun', 'monday', 'friday', 'tuesday', 'thursday', 'saturday', 'wednesday']

引数reverseと併用して、文字数の多い要素の順に並び替える例である。

sorted(d, key=lambda x: len(x), reverse=True)
['wednesday', 'thursday', 'saturday', 'tuesday', 'monday', 'friday', 'sun']

要素を降順に並び替える。今回は、リストdの要素そのものが並び替えられる。

d.sort()
d
['friday', 'monday', 'saturday', 'sun', 'thursday', 'tuesday', 'wednesday']

要素を昇順に並び替える。

d.sort(reverse=True)
d
['wednesday', 'tuesday', 'thursday', 'sun', 'saturday', 'monday', 'friday']

引数keyに関数を指定して、要素の並べ方を変更できる。

d.sort(key=lambda x: len(x))
d
['sun', 'monday', 'friday', 'tuesday', 'thursday', 'saturday', 'wednesday']

6.6. 要素の所属検査#

ある要素がリストに含まれるかを調べる。

'sun' in d
True

ある要素がリストに含まれないかを調べる。

'sun' not in d
False

6.7. リストと参照#

6.7.1. 参照の代入#

リストxを作成する。

x = ['mon', 'tue', 'wed', 'thu', 'fri']
x
['mon', 'tue', 'wed', 'thu', 'fri']

変数yを作成し、リストであるxを代入する。

y = x
y
['mon', 'tue', 'wed', 'thu', 'fri']

リストyに要素を追加してみる。

y.append('sat')
y.append('sun')
y
['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']

(予想に反し)リストxの要素も変更されている('sat''sun'が追加されている)。

x
['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']

これは、代入文y = xが「変数yを定義し、変数yの参照先を変数xの参照先と一致させる」という動作を行うからである。元々、変数xはリストオブジェクトを参照していた(関連付けられていた)ので、そのリスト・オブジェクトに複数の変数xyを経由してアクセスできるようになった。ひとつのオブジェクトの実体を複数の変数xyが参照している状況であるので、いずれの変数を経由してもオブジェクトの内容を変更できるし、いずれの変数を評価しても同じ結果が得られる。

6.7.2. リストのコピー#

先ほどと同様に、リストxを作成する。

x = ['mon', 'tue', 'wed', 'thu', 'fri']
x
['mon', 'tue', 'wed', 'thu', 'fri']

変数yを作成し、リストであるxの全体のスライスx[:]を代入する。このとき、xのリスト・オブジェクトのコピーが作成され、変数yはコピーを参照することになる。

y = x[:]
y
['mon', 'tue', 'wed', 'thu', 'fri']

先ほどと同様に、リストyに要素を追加してみる。

y.append('sat')
y.append('sun')
y
['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']

今回は、リストxの要素は変更されない。これは、変数xyが、それぞれ別のリスト・オブジェクトを参照しているからである。

x
['mon', 'tue', 'wed', 'thu', 'fri']

6.8. リストと関数の引数#

引数で与えられたリストの末尾に'END'を追加する関数append_endを定義する。

def append_end(s):
    s.append('END')

以下のリストxを引数として、関数append_endを呼び出す。

x = ['one', 'two']
x
['one', 'two']
append_end(x)

すると、呼び出し元の変数であるxの内容が変化する。

x
['one', 'two', 'END']

数値や文字列を引数として関数に渡す場合と異なり、リストを引数として関数に渡すと、その関数内でそのリストに対する変更は、関数の呼び出し元にも波及する。

6.9. リストの様々な作成方法#

range関数が表す範囲に対応するリストを作成するには、list関数を用いる。

a = list(range(10))
a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = list(range(1, 10, 2))
a
[1, 3, 5, 7, 9]

リストの要素はデータ型が異なってもよい。

b = [1, 'one', 'first']
b[0]
1
b[1]
'one'

(0を含む)自然数のリストをrange関数で作成する。

a = list(range(10))
a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

6.9.1. リストの内包表記#

空のリストを作成する。

a2 = []
a2
[]

空のリスト(a2)に要素を追加する

for i in range(10):
    a2.append(i * 2)
a2
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

これは2の段の掛け算の結果を表している。

a2[7]
14

上記の処理はリストの内包表記を使って簡潔に書ける。ソースコードを左から"list of i times two for all i in the range of ten"のように英語読みをすると解釈しやすい。

a2n = [i * 2 for i in range(10)]
a2n
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
a2n[7]
14

3の段の掛け算も内包表記を使って簡潔に書ける。

[i * 3 for i in range(10)]
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

内包表記を入れ子にして、掛け算表を2次元配列(リスト)として作成する。

m = [[i * j for j in range(10)] for i in range(10)]
m
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18],
 [0, 3, 6, 9, 12, 15, 18, 21, 24, 27],
 [0, 4, 8, 12, 16, 20, 24, 28, 32, 36],
 [0, 5, 10, 15, 20, 25, 30, 35, 40, 45],
 [0, 6, 12, 18, 24, 30, 36, 42, 48, 54],
 [0, 7, 14, 21, 28, 35, 42, 49, 56, 63],
 [0, 8, 16, 24, 32, 40, 48, 56, 64, 72],
 [0, 9, 18, 27, 36, 45, 54, 63, 72, 81]]
m[2]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
m[2][3]
6

6.9.2. 2次元配列作成時の注意#

*演算子を使うと、指定した値で初期化しながら、指定した要素数のリストを作ることができる。

a = [0] * 10
a
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

これを拡張して、2次元配列を作ることもできるが、これは避けるべきである。

a = [[0] * 10] * 10
a
[[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, 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]]

一見すると2次元配列が作成されたように見えるが、思わぬ落とし穴がある。以下のコードである要素を変更したつもりが、列全体の値が変更されてしまう。

a[3][5] = 2
a
[[0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 2, 0, 0, 0, 0]]

これは、[0] * 10で1次元のリスト・オブジェクトが一つだけ作成され、その唯一のオブジェクトへの参照が全てのa[.]にセットされただけであるからである。

指定したサイズのリストを作成するときに、内包表記を使うこともできる。

b = [0 for i in range(10)]
b
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

これを拡張して、2次元配列を作ることができる。

b = [[0 for j in range(10)] for i in range(10)]
b
[[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, 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]]

こちらは、我々が通常期待する2次元配列として振る舞う。

b[3][5] = 2
b
[[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, 2, 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]]