9. 複数の値をまとめて扱おう

どんなプログラムも、順次・分岐・反復で構成されると学んできました。 構造を掴んだ後は、各部でどんな処理をしているのか、詳しく知りたくなりますよね。 そんなときに知っていると役に立つ事項を、この章と次章で紹介します。

本章では、複数の値の扱い方 を学びましょう。 対話モードで手を動かしながら読み進めてください。

9.1. 複数の値をリストにまとめる

9.1.1. リストを作る

8 章for 文を使って、決まった回数繰り返す」で、リストを「値をまとめた値」と紹介しました。 数値をまとめたリスト([1, 2, 3])や、 文字列をまとめたリスト(["susumuis", "nao_y", "nikkie"])を覚えていますか。

型が揃っていなくても リストに値をまとめられます。

>>> [1990, 175.2, "nikkie"]  # ((整数、小数、文字列からなるリスト))
[1990, 175.2, 'nikkie']

リストを作るときの [1, 2, 3]式で、評価した結果がリスト です。 これまで扱ってきた式と同じように、変数が使えます 。 評価結果は、変数が指す値がまとまったリストです。

>>> a, b = 4, 6
>>> [a, b]
[4, 6]

9.1.1.1. リストの型

リストを表す型は、list (リスト)です。

>>> type([1, 2, 3])  # (([1, 2, 3]という値の型を確認))
<class 'list'>

リストを作るときに使う []角括弧、ブラケット)で、作る値がリストだと 指定 しています。 そのため、まとめる値がどんな型でも、リストであれば常に型は list です。

>>> type([1990, 175.2, "nikkie"])
<class 'list'>

9.1.1.2. リストを変数で指す

数値や文字列と同じように、リストも 変数に代入できます

>>> kazu_list = [1, 2, 3]

リストを指していても、そもそも変数なので、関数の引数に使えます

>>> type(kazu_list)
<class 'list'>
>>> print(kazu_list)  # ((引数のリストが表示される))
[1, 2, 3]

9.1.1.3. 練習問題

  1. 自由にリストを作り、変数に代入してください。まとめる値の型を揃えても、揃えなくてもかまいません

  2. 変数が指す値の型を確認しましょう

  3. 変数が指しているリストを確認しましょう

[解答]

1では、kazu_list を作ったとします。

2は type 関数に渡せばいいですね。

3は print 関数に渡す、もしくは対話モードなので、変数を評価すればいいです。

>>> kazu_list
[1, 2, 3]

9.1.2. リストの個々の値を扱う

リストにまとめられた個々の値を「要素」と呼びます。 リストの特徴は、要素が順番を持つ ことです。 8 章for 文を使って、決まった回数繰り返す」で説明したように、一番左が先頭の要素で、一番右が末尾の要素です。

9.1.2.1. インデックス

何番目の要素かを指定 することで、個々の要素を扱えます。 何番目かの指定には、「インデックス」という 番号 を使います。 インデックスは整数で、正の値も負の値も取れます。

楽器名のリストを使ってインデックスに慣れましょう。

>>> gakki_list = ["チューバ", "ユーフォニアム", "コントラバス"]
>>> gakki_list
['チューバ', 'ユーフォニアム', 'コントラバス']
  • 0と正の値は 先頭から数えた番号

  • 負の値は 末尾から数えた番号

楽器名のリストのインデックス

楽器名(要素)

先頭から数えたインデックス

末尾から数えたインデックス

"チューバ"

0

-3

"ユーフォニアム"

1

-2

"コントラバス"

2

-1

../_images/10-3_list_index.drawio.png

インデックスには前からと後ろからの2通りがある

9.1.2.2. インデックスで要素を指定する

リストの要素は リスト[インデックス] という書き方で指定します。 これも式で、評価結果はインデックスに対応する要素です。

>>> ["チューバ", "ユーフォニアム", "コントラバス"][0]
'チューバ'

リスト の部分は、リストを指す変数 も使えます。 変数を使うと、この式はまず変数が評価されます。 試しに gakki_list が指すリストから、先頭(インデックス 0)の要素を指定してみましょう。

>>> gakki_list[0]  # ((["チューバ", "ユーフォニアム", "コントラバス"][0] と同じ))
'チューバ'

負のインデックスを使って、末尾の要素(インデックス -1)を指定します。

>>> gakki_list[-1]
'コントラバス'

リスト[インデックス] という書き方は、リストを指す変数で書くことがほとんどです。 この書き方に慣れると、「リスト gakki_list の先頭の要素 gakki_list[0]」とも考えられますね。

また、リストを指す変数[インデックス]変数を書いたのと同じ とも言えます。 インデックスのところの gakki_list の図をもう一度見てください。 gakki_list のインデックス 0gakki_list[0]が指す 値が "チューバ" です。 他のインデックスも値を指します。 つまり、リストを指す変数は、インデックスを指定することで、複数の変数として使えるのです。

9.1.2.3. 練習問題

  1. gakki_list の要素 "ユーフォニアム" を正のインデックスで指定してみましょう

  2. 続いて、 gakki_list から "コントラバス" も正のインデックスで指定してみましょう

  3. 負のインデックスを使って、gakki_list の要素を1つ指定してみましょう

[解答]

>>> gakki_list[1]  # ((1の解答))
'ユーフォニアム'
>>> gakki_list[2]  # ((2の解答))
'コントラバス'
>>> gakki_list[-2]  # ((3の解答))
'ユーフォニアム'

インデックスの表や図と合わせて確認し、インデックスに慣れてくださいね。

[コラム] プログラマーは0から数え始める

インデックスとそれを使った要素の指定を紹介しました。 日常生活でものを数えるとき、1、2、3と1から数え始めますが、 プログラミングでは(前からの) インデックスは0から 始まります。

プログラマーの世界では、最初を表すインデックスが0 なのです。 これは決まりですから、慣れるのが一番です。 プログラミングに取り組むときは、「0から数え始める」と考え方を切り替えましょう。

リスト[1] と書いてしまい、先頭の要素が指定できないというのは、初めはみな経験します。 日常生活の中で、最初は1というのが染み付いているのです。

[コラム] インデックスの指定には正負2通りのやり方がある

同じ要素を、正のインデックスでも負のインデックスでも指定できます。 リストは先頭から末尾への順番で扱うことが多いので、0以上の整数を使った方法はよく使われます。 ですが、「後ろから2番めの要素」のように、後ろのほうが考えやすい時があり、そのときはマイナスの整数の出番です。 2通りのうちどちらを使ってもいいので、考えやすい方を使うのをおすすめします。

[発展] リストの「長さ」とインデックス

リストは 要素の数 を「長さ」として持ちます。 3つの要素を持つリスト gakki_listlen 関数に渡してみましょう。

>>> len(gakki_list)
3

リストの長さは、インデックスに指定できる整数の範囲 を決めています。

  • 先頭から指定する場合、インデックスは 0 以上、長さ-1 以下

  • 末尾から指定する場合、インデックスは -長さ 以上、-1 以下

インデックスに指定できるのは -長さ 以上、長さ-1 以下の整数です。 長さと等しい整数は指定できない ので注意しましょう。

[発展] インデックスに指定できない整数を指定するとエラー

インデックスに指定できない整数を指定した場合、処理系は IndexError を出します。 gakki_list の指すリストを例に、インデックスに指定できる整数の範囲外の値(3-4)を試してみましょう。

>>> gakki_list[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> gakki_list[-4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

「list index out of range」は、リストのインデックス(index)が範囲外(out of range)という意味です。

IndexError を見かけたら、len 関数でリストの長さを確認し、指定できる範囲内のインデックスを指定していたか確認しましょう。

9.2. リストを変更する

リストは複数の値をまとめただけでなく、「変更」できます。

  • リストの個々の要素を変更する

  • リスト自体を変更する(要素の追加/削除)

9.2.1. リストの要素を変更する

インデックスで指定した要素に 別の値を代入する ことで、その要素を変更できます。 リスト[インデックス] には変数のように指す値がありますが、それを更新できます。

例として gakki_list"ユーフォニアム""ユーフォ" に変更してみます。 ユーフォニアムという楽器は、縮めて「ユーフォ」と呼ばれます。

>>> gakki_list[1]  # ((変更する前の値を確認します))
'ユーフォニアム'
>>> gakki_list[1] = "ユーフォ"
>>> gakki_list[1]
'ユーフォ'

リスト全体を表示しましょう。 インデックス 1 の要素が指す値は更新されていますが、 他の要素が指す値(チューバやコントラバス)は更新されていませんね。

>>> gakki_list
['チューバ', 'ユーフォ', 'コントラバス']

9.2.1.1. 練習問題

コントラバスも、縮めた呼び方「コンバス」に変えてみましょう。

[解答]

>>> gakki_list[-1] = "コンバス"
>>> gakki_list
['チューバ', 'ユーフォ', 'コンバス']

[コラム] インデックスの指定し忘れに注意!

要素を変更する時に、過ってインデックスを指定し忘れると、変数が指す値をリストから文字列に更新 してしまいます。 変数がリストを指さなくなるということです。

>>> gakki_list2 = ["チューバ", "ユーフォニアム", "コントラバス"]  # ((別のリストを作って確認))
>>> gakki_list2
['チューバ', 'ユーフォニアム', 'コントラバス']
>>> gakki_list2 = "ユーフォ"
>>> gakki_list2
'ユーフォ'

リストの要素を変えたい場合は、インデックス自体の指定し忘れに注意しましょう。

9.2.2. リスト自体を変更する

9.2.2.1. リストに要素を追加する

楽器はまだまだありますから、 gakki_list に追加したいですよね。 リスト.append(追加したい値) と書くと、リストの末尾に値を追加 できます。 appendという英単語は「追加する」という意味です。

リストに対して使える、末尾に値を追加する関数

箱の名前

append

箱は何をするか

リストの末尾に要素を追加する

箱に入れるもの

リストに追加したい値

箱から出てくるもの

なし

トランペットを追加してみます。

>>> gakki_list  # ((要素を追加する前の確認))
['チューバ', 'ユーフォ', 'コンバス']
>>> gakki_list.append("トランペット")
>>> gakki_list
['チューバ', 'ユーフォ', 'コンバス', 'トランペット']

要素が4つに増えたことで、インデックスに指定できる値の範囲も -4 から 3 までに変わりました。 追加した要素をインデックスを使って指定します。

>>> gakki_list[3]  # ((-1 と指定してもよい))
'トランペット'

append のように 特定の型の値についてのみ適用できる関数 を「メソッド」と呼びます。

9.2.2.2. 練習問題

append を使って gakki_list に好きな楽器を1つ追加してみましょう。 楽器名の例:オーボエ、フルート、ホルン、トロンボーン、クラリネット

[解答]

>>> gakki_list.append("オーボエ")
>>> gakki_list
['チューバ', 'ユーフォ', 'コンバス', 'トランペット', 'オーボエ']

[発展] append を使って100個の要素のリストを作る

100個のように、多くの要素を持つリストは append繰り返し使って 作れます。

まず要素を持たないリスト(空のリスト)を用意します。

>>> list_100 = []

そして、要素の追加を反復します。

>>> for i in range(100):
...     list_100.append(i)
...

対話モードで for 文を書く際は、改行して現れる ... の表示の後に、再度 Enter キーを押す必要があります。

長さを求めると、100個の要素を持つことが確認できます(前節の「発展」参照)。 これは0〜99までの整数のリストです。

>>> len(list_100)
100

空のリストを用意し、繰り返し append。 これを使えば、数千、数万の要素を持つリストも作れますね。

9.2.2.3. リストから要素を削除する

追加以外に削除もできます。 リスト.pop(削除したい要素のインデックス) で、指定したインデックスの要素を削除 できます。 popという英単語は「ポンと出す」という意味です。 指定したインデックスの要素が 取り出され (返り値)、リストからは消えます。

リストに対して使える、指定したインデックスの要素を取り出す関数

箱の名前

pop

箱は何をするか

指定したインデックスの要素を取り出し、リストから削除する

箱に入れるもの

取り出したい要素のインデックス(指定しない場合は末尾を指定したことになる)

箱から出てくるもの

取り出した要素

ユーフォニアム(インデックス 1 の要素)を取り出してみます。

>>> gakki_list2 = ["チューバ", "ユーフォニアム", "コントラバス"]
>>> gakki_list2.pop(1)  # ((インデックス1の要素が返り値になっている))
'ユーフォニアム'
>>> gakki_list2
['チューバ', 'コントラバス']

取り出された要素以降の正のインデックスは1ずつ小さくなります。

../_images/10-7_index_when_pop.drawio.png

"ユーフォニアム" がリストからなくなり、要素と対応するインデックスが変わる

pop引数を指定せずに 呼び出せます(gakki_list2.pop())。 その場合、末尾の要素 が取り出されて、リストから削除されます。

引数に範囲外のインデックスを指定すると、処理系は IndexError を出します。

9.2.2.4. 練習問題

pop を使って gakki_list から要素を1つ削除してみましょう。

[解答]

参考例として末尾の要素を削除しました。 お好きなインデックスを指定して試してみてください。

>>> gakki_list.pop()
'オーボエ'
>>> gakki_list
['チューバ', 'ユーフォ', 'コンバス', 'トランペット']

9.3. リストの使い所

ここまで、リストの以下の特徴について見てきました。

  • ◯番目として、個々の値を扱える

  • リストを変更できる

では、そんなリストをプログラミングでどんな時に使うか考えてみましょう。 私たちの身の回りには、リストが使えるシーンが隠れています。

9.3.1. 例:ファミリーレストランの会計

例として、ファミレスでの注文の扱いを考えます。

  • 注文があるたびに 商品の金額をリストに追加 します

  • 会計時にリストを使って合計金額を算出します

リストを使って、処理中の値を覚えておく わけです。

9.3.1.1. 注文を受ける

お客さまが入店したら、空のリスト を用意します。 お客さまごとに会計をするので、空のリストはお客さまごとに必要です。 話を単純にするために、お客さまは1組だけとします。

変数 chumon_kingaku で、お客さまの注文金額を表すことにしましょう。

>>> chumon_kingaku = []

お客さまはドリアとドリンクバーを注文しました。

  • ドリア:300円(※金額はすべて税込価格とします)

  • ドリンクバー:180円

chumon_kingaku に追加します。

>>> chumon_kingaku.append(300)
>>> chumon_kingaku.append(180)

お客さまは食後にデザートを追加で注文しました。 デザートは、プリンとティラミスのセットプレート(400円)です。

>>> chumon_kingaku.append(400)

9.3.1.2. 会計する

お客さまは会計に進みました。 リスト chumon_kingakufor 文を使うと、合計金額が求められます。 最初に合計金額を0円とし、chumon_kingaku の要素(個々の金額)を合計金額に足すことを繰り返します。

>>> goukei_kingaku = 0
>>> for kakaku in chumon_kingaku:
...     goukei_kingaku += kakaku
...
>>> goukei_kingaku  # ((計算された合計金額))
880

価格それぞれを足すという反復処理の前に、合計金額を0円としておく のがポイントです。 0円は実際の合計金額ではありませんが、そこに値を加算していきます。 反復処理が終わったときには、リスト chumon_kingaku要素が1度ずつ足されている0 + 300 + 180 + 400)ので、 goukei_kingaku が指す値は、注文の合計金額に等しくなります。

9.3.1.3. 練習問題

ここで見た例にならって、あなたの直近の注文をリストで扱い、合計金額を算出してみてください。

[解答]

この練習問題の解答は省略します。 レストランの例を参考に取り組んでみてください。

[発展] 「配列」という言葉

プログラミングには「配列(はいれつ)」という言葉があります。 配列は複数の値をまとめて扱うために使われます。

配列は、この章で紹介した リストとほぼ同じ ものです。 配列とリストの違いは、配列では要素は同じ型に限るという点です。 プログラミングに取り組む中で配列という言葉に出会ったら、リストに読み替えてください。

[発展] 複数の値のもう1つの扱い方、タプル

リストと似た値に タプル があります。 リストでは複数の値を [] で囲みましたが、タプルは () で囲みます。 () で囲むことで、作る値がタプルだと指定しています。

>>> (1, 2, 3)
(1, 2, 3)

タプルの型は、要素の型によらず、常に tuple です。

>>> type((1990, 175.2, "nikkie"))
<class 'tuple'>

[発展] タプルとリストの共通の性質

  • タプルも要素に 順番 を持ちます(左が先頭、右が末尾)

  • for 文でタプルの要素を 反復 できます

  • 要素数を 長さ として持ちます

  • -長さ 以上、長さ-1 以下の範囲の インデックス で要素を指定できます

  • インデックスに指定できる範囲の外を指定した場合、処理系は IndexError を出します

>>> gakki_tuple = ("チューバ", "ユーフォニアム", "コントラバス")
>>> gakki_tuple[-2]  # ((後ろから2つ目の要素を指定))
'ユーフォニアム'

リストのインデックスで試したことを、タプルでも試してみてください。

[発展] タプルは変更できない!(リストとの違い)

タプルは、要素への代入、要素の追加、削除ができません。 例えば、インデックスが指す要素に代入しようとすると、処理系はエラーを出します。

>>> gakki_tuple[1] = "ユーフォ"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

TypeError は、タプルは要素への代入ができない(does not support item assignment)旨を表示しています。 代入のほか、append で要素を追加したり、pop で削除することもできません(そのようなメソッドが用意されていません)。

[コラム] リストとタプルの使い分け

リストもタプルも for 文で繰り返したり、インデックスで要素を指定したりできます。 使い分けに影響するのは、変更できる必要があるかどうかです。 私は、変更しない複数の値をタプルで 扱い、変更する可能性がある複数の値をリストで 扱っています。

リストは空のリストに繰り返し append する例を紹介したように、変更できるのが利点です。

タプルは、用意した後に変更しない(または変更すると困る)複数の値を扱う際に利用します。 変更しないタプルにすることで、変更を意図していないことを示す意図もあります。

[発展] 複数の返り値を返す関数は、タプルを返している

5 章関数を作って処理をまとめよう」の発展で 「関数の返り値は複数あってもいい」と紹介しました。 返り値をカンマで区切って書いたとき、実は タプルを返して いるんです。

>>> kaerichi = aisatsu_bun_kai("nao_y")
>>> type(kaerichi)
<class 'tuple'>
>>> kaerichi
('nao_yさん、ごきげんよう', 14)

複数の返り値を持つ関数の返り値を変数に代入するとき、 返り値の数と同じだけ変数を用意した場合は、それぞれの値が変数に代入されます。 変数を1つだけ用意した場合は、返り値を並べたタプル が代入されます。

[発展] 同じ値を指すことの不思議(数値や文字列の場合)

変数2 = 変数1 という文で、変数1が指す値そのものを変数2も指すのでしたね。

復習として、整数の場合を見てみましょう(文字列の場合も同じです)。

>>> a1 = 4
>>> a2 = a1
>>> a1
4
>>> a2
4

a1 が指す 4 という値を a2 も指しています。 ここで a1 が別の値を指すように変更しても a2 は変わりません。

>>> a1 += 1
>>> a1
5
>>> a2
4

[発展] 同じ値を指すことの不思議(リストの場合、数値や文字列と違う!)

変数2 = 変数1変数1 が指す値がリストだと、リスト自身を変更できる ために、ちょっと不思議なことが起こります。

>>> a = [1, 2, 3]
>>> b = a
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]

a が指すリストを b も指しています。 a が別のリストを指すように変更(例 a = [4, 5])しても b は変わりません(整数で見たときと同じです)。

違いは、a が指す リストそのものを変更 する場合です(append メソッドなど) 。 これはリストのときに限った話で、数値や文字列の場合は、値そのものを変更することができません (4 という値はどこまでいっても 4 です)。

a の指すリストで、インデックス 1 が指す値を更新してみましょう。

>>> a[1] = 102
>>> a
[1, 102, 3]

ab同一のリスト を指しているので、リストが変更されたことを変数 b の側からも確認できます。

>>> b
[1, 102, 3]

変数 a と変数 b指すリストが同じ というのがポイントです。 逆に、 b.append(104) のようにリストを変更した場合も、a の側からも変更が確認できます。

../_images/10-4_reference_same_list.drawio.png

ab が指す共通のリストの変更となる

[発展] 2つの変数が同じリストを指さないようにする

1つ前の発展の b = a の例は、同じリストを指さなければ起こりません

続く例の cd の指すリストは、個々の要素が同じですが、別々に作られたのでリストとしては別物 です。

>>> c = [4, 5]
>>> d = [4, 5]
>>> c[0] = 104
>>> c
[104, 5]
>>> d
[4, 5]
../_images/10-5_reference_each_list.drawio.png

c の指すリストのみの変更。d の指すリスト(c と同じ要素)には影響しない

b = a の例は、1つのリストを複数の変数で指して使いたい場合 の書き方です。 同じ要素のリストの 片方についてだけ操作したい場合 は、変数 cd の例のように リストをそれぞれ代入 しましょう。 リスト.copy() というメソッドを使っても同じ結果になり、片方のリストだけを操作できます。

リストに対して使える、コピーした別のリストを用意する関数

箱の名前

copy

箱は何をするか

リストをコピーし、同じ要素からなる別のリストを作る

箱に入れるもの

なし

箱から出てくるもの

コピーしたリスト

>>> e = [1, 2, 3]
>>> f = e.copy()
>>> e
[1, 2, 3]
>>> f
[1, 2, 3]
>>> e[1] = 102  # ((変数fが指すリストには影響しない))
>>> e
[1, 102, 3]
>>> f
[1, 2, 3]