連載
» 2019年05月31日 05時00分 公開

Python入門:[Python入門]リストの基本 (4/4)

[かわさきしんじ,DeepInsider編集部]
前のページへ 1|2|3|4       

リスト内包表記

 次に「リスト内包表記」と呼ばれる記法を使って、リストを作成する方法を簡単に見ておこう。

 まず、0〜9の整数値を要素とするリストを作成したいものとする。これには、「list関数によるリストの作成」で述べたように、list関数を使って「list(range(10))」などとするのが簡単だが、ここでは頭の体操として、list関数を使わない方法を少し考えてみよう。

 簡単なのは次のように直接書いてしまうことだ。

intlist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

直接要素を記述する

 この方法には欠点がある。要素数が多くなると、手で全ての要素を入力するのが面倒くさいということだ。0〜99までの整数値を要素とするリストを作るのに、そんなことはしたくない。そこで、for文を使って繰り返し処理を行うというのが一つの手として考えられる。

intlist = []
for num in range(10):
    intlist.append(num)

print(intlist)

for文でリストに要素を追加していく

 これは最初に空のリストを用意して、そこに「リストへの要素の追加」で紹介したappendメソッドを使って要素を1つずつ追加していくというやり方だ。これなら、リストの要素数が多くなっても書くコードの量は変わらない(range関数に渡す引数の値を変更するだけだ)。とはいえ、定形的に計算できる値を要素とするリストを作成するのにこれだけの量のコードを書くのは面倒くさい。そこで、上と同様なリスト作成をより簡潔に記述できるのが、先ほど述べた「リスト内包表記」だ。これを使うと、上のコードは次のようになる。

intlist = [num for num in range(10)]

リスト内包表記によるリストの作成(その1)

 注目してほしいのは、強調書体として示した「for num in range(10)」という部分だ。これはその前のfor文でリストを作成したときと同じ記述だ。最初は分かりにくいのだが、for文と内包表記の対応関係を頭に入れておくと、後から(自分か他者が書いた)入り組んだ内包表記を目にしたときにも、それを基に何をしているかを解読できるようになる。対応関係をまとめると次のようになる。

for文とリスト内包表記の対応 for文とリスト内包表記の対応

 重要な点は「appendメソッドでリストに追加するものをfor節の前に置く」「for節の内容はfor文の先頭行と同じ」となる。この対応関係を覚えておくと、リスト内包表記が分かりやすくなるはずだ。

 内包表記の書き方を一般化すると次のようになる。

リスト内包表記

[要素を算出する式 for ループ変数 in 反復可能オブジェクト if 条件]


 「要素を算出する式」という部分には、主に「ループ変数」を用いた式を記述する。「if」がある場合には、「ループ変数」を用いて記述した条件が真(True)となる場合にだけ、「要素を算出する式」が計算されて、その結果がリストの要素となる。このとき、内包表記を構成する「for」や「if」を、「for節」とか「if節」とかなどと表現する。


 先ほどの例では「要素を算出する式」には「num」と「ループ変数」と同じものが書かれているだけだった。そのため、最終的なリストはrange関数によってループ変数に渡される0〜9の整数値となった。全ての要素を2倍したければ、「要素を算出する式」に「num * 2」と書けばよい。

 また、最後にif句を付加して、特定の条件を満たしたときにだけリストの要素となる値を計算するようにもできる。例えば、「ループ変数の値が偶数のときだけ」という条件を付加するのであれば、「if num % 2 == 0」と付ければよい。

 以下に幾つか例を示す。

print([num * 2 for num in range(10)])  # 0、2、4、……、18を要素とするリスト
print([num * num for num in range(10) if num % 2 == 0])  # numが偶数のときに二乗

リスト内包表記によるリストの作成(その2)

 最初の例は、「要素を算出する式」に「num * 2」と書いたので、0、2、4、……、18がリストの要素となる。次の例は、「if」に続けて「num % 2 == 0」とあるのでループ変数numの値が偶数のときにだけ「num * num」の値を計算して、それらをリストの要素とする。実行結果を以下に示す。

実行結果 実行結果

 ここまでの例は比較的分かりやすいものだが、forやifを深くネストさせることも可能だ。例えば、以下のコードで使っているリスト内包表記は何をするものだろう。

for row in [[x * y for y in range(1, 10)] for x in range(1, 10)]:
    print(row)

これは何をするコード?

 実行結果は次のようになる。

掛け算の九九を段ごとにリストとしている 掛け算の九九を段ごとにリストとしている

 ご覧の通り、掛け算の九九を表にしたものだ(これは「リストを要素とするリスト」になっている。後述)。なぜ、こうなるかを少し考えてみよう。

 「[[x * y for y in range(1, 10)] for x in range(1, 10)]」の先頭部分「[x * y for y in range(1, 10)]」を「z」に置き換えてみよう。すると、最初のリスト内包表記は「[z for x in range(1, 10)]」のように書ける。「z」は「[x * y for y in range(1, 10)]」であり、両方ともリスト内包表記となっているので、ここでは前者を「外側のリスト内包表記」と、後者(z)を「内側のリスト内包表記」と呼ぼう。

 ここで、先ほど見た対応関係を基に、「外側のリスト内包表記」つまり「[z for x in range(1, 10)]」をfor文に展開すると次のようになる。簡単にいえば「for節はfor文にして、その本体でappendメソッドでzをリストに追加していく」コードになる。

result = []
for x in range(1, 10):
    # zを計算
    result.append(z)

外側のリスト内包表記をfor文に展開したもの

 そして、「z」というのは「[x * y for y in range(1, 10)]」という内側のリスト内包表記だった。これも同様にして、次のように書き直せる(上のコードの「Xを計算」という部分に入るコード)。

z = []
for y in range(1, 10):
    z.append(x * y)

内側のリスト内包表記をfor文に展開したもの

 これら2つのfor文をまとめると、次のようになる。

result = []
for x in range(1, 10):
    # zを計算
    z = []
    for y in range(1, 10):
        z.append(x * y)
    result.append(z)

先ほどの二重のリスト内包表記でやっていることをfor文に展開したもの

 最初のfor文のループ変数(x)が九九のどの段の計算をしているかを管理し、内側のfor文ではその段の値に1〜9を掛けた結果を「z」に追加している。内側のfor文が9回実行されると、その段の掛け算の結果を要素とするリストzが得られる。外側のfor文では、これを変数resultにappendメソッドで追加し、今度は次の段の処理を行う。外側のfor文が9回実行されて終了すると、最終的な九九の表が変数resultに出来上がっているというわけだ。

 上の二重ループも内包表記を使えば、「[[x * y for y in range(1, 10)] for x in range(1, 10)]」と書けてしまう。分かりやすいかといえば、そうでもないが、このような記述も可能ということは覚えておこう。同様に、ifもネストさせることが可能だ(例は省略する)。

 最初のうちはリスト内包表記はなかなか分かりにくいかもしれないが、そのときには上で見たのと反対の作業を行ってみよう。つまり、最初は内包表記として書ける定形パターンとなるようにfor文を書いて、そこから内包表記に置き換えてみる。慣れてくれば、for文から内包表記に書き換えるのではなく、内包表記を直接書けるようになっていくだろう。逆に、難解な内包表記があれば、for文に展開していくことで、そこでやっている内容を把握できるはずだ。

リストのリスト

 先ほどのリスト内包表記や、それをfor文に展開したものの例では「リストを要素とするリスト」が出てきていた。分かりやすくここではfor文に展開したものを再掲する。

result = []
for x in range(1, 10):
    # zを計算
    z = []
    for y in range(1, 10):
        z.append(x * y)
    result.append(z)

九九の表を作成する二重のfor文

 このリストでは、外側のfor文で空のリストzを用意して、内側のfor文でそのリストにappendメソッドにより要素追加して、最後にそれをリストresultへappendメソッドで追加していた。これにより、「リストを要素とするリスト」を作成していた。

上のコードで作成される「リストを要素とする」リスト 上のコードで作成される「リストを要素とする」リスト

 これは9×9の行列のようにも見える。このとき、横方向のまとまりのことを「行」(row)と、各行の中で同じインデックス位置にあるものを「列」(column)と呼ぶこともある(以下ではループ変数にこれらの名前を付けている)。

 ここで、変数resultが参照するリストの最初の要素(九九の表の「一の段」)にアクセスするには「result[0]」とすればよいことは既にお分かりだろう。では、その最初の要素(1×1の答え)にアクセスするには、どうすればよいだろう。これには「result[0]」に続けて、要素となっているリストの要素にアクセスするためのインデックスを指定すればよい。以下に例を示す。

print(result[0][1])

九九の表の「1×2」の答えは?

 実行結果を以下に示す。

「1×2」の答えは「2」 「1×2」の答えは「2」

 また、要素となっているリストをfor文で取り出したければ、for文をネストさせる。以下に例を示す。

for row in result:
    for column in row:
        print(f'{column:>3} ', end='')
    print()

九九の表をきれいに書式化して出力するコード

 ここでは、フォーマット済み文字列に書式指定を行い、九九の表をキレイな形で出力するようにしている。実行結果を以下に示す。

実行結果 実行結果

まとめ

 今回は、Pythonでデータを扱う上で一番よくお世話になるであろうリストの基本を取り上げた。次回は、リストを操作する各種のメソッドを取り上げる。

今回のまとめ:リストの基本

  • リストは[]内にその要素をカンマ(,)区切りで並べていく
  • リストの要素を取り出すにはリスト(を代入した変数)に[インデックス番号]を付加する
  • 「リスト[インデック番号] = 新しい値」のようにして代入することで、リストの要素を変更できる
  • リストに要素を1つだけ追加するにはappendメソッドが使える
  • あるいは、「+」演算子を使ってリストを結合する
  • リスト以外のものをリストには結合できない
  • リストから要素を削除するには、削除したい「値」を指定してremoveメソッドを呼び出す
  • リスト内に同じ値が複数含まれている場合、removeメソッドはインデックスの小さい方の要素を削除する
  • あるいはdel文を使用して、「del リスト[インデックス番号]」とする
  • 「for ループ変数 in リスト:」により、繰り返しのたびにリストの各要素がループ変数に代入される

「Python入門」のインデックス

Python入門

前のページへ 1|2|3|4       

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。