Pythonのリスト内包表記の使い方【if, if-else, ネスト】

Pythonのリスト内包表記の構文をまとめています。

Pythonのリスト内包表記の使い方【if, if-else, ネスト】

リスト内包表記の構文まとめ(急いでいる人向け)

リスト内包表記は、リストやタプルなどのイテラブルから変数を取り出して、の中で操作をしてリストに格納します。

  • 基本構文:[式 for 変数 in イテラブル]
  • ifを使った構文:[式 for 変数 in イテラブル if 条件式]
  • if-elseを使った構文:[式1 if 条件 else 式2 for 変数 in イテラブル]
  • リストをネストさせる構文:[[式 for 変数2 in イテラブル2] for 変数1 in イテラブル1]
# 基本構文
l = [i for i in range(10)]
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


# ifを使った構文
l = [i for i in range(10) if i % 2 == 0]
print(l)
# [0, 2, 4, 6, 8]


# if-elseを使った構文
l = ['偶数' if i % 2 == 0 else '奇数' for i in range(10)]
print(l)
# ['偶数', '奇数', '偶数', '奇数', '偶数', '奇数', '偶数', '奇数', '偶数', '奇数']


# リストをネストさせる構文
l = [[i * j for j in range(1, 10)] for i in range(1, 10)]
print(l)
"""
[[1, 2, 3, 4, 5, 6, 7, 8, 9],
 [2, 4, 6, 8, 10, 12, 14, 16, 18],
 [3, 6, 9, 12, 15, 18, 21, 24, 27],
 [4, 8, 12, 16, 20, 24, 28, 32, 36],
 [5, 10, 15, 20, 25, 30, 35, 40, 45],
 [6, 12, 18, 24, 30, 36, 42, 48, 54],
 [7, 14, 21, 28, 35, 42, 49, 56, 63],
 [8, 16, 24, 32, 40, 48, 56, 64, 72],
 [9, 18, 27, 36, 45, 54, 63, 72, 81]]
"""

リスト内包表記とは?

Pythonのリスト内包表記とは、リストを簡潔に生成する表記法です。

主な用途としては、リストのようなイテラブルのそれぞれの要素になにか操作を行った上で、その結果をもとに再度リストを作ることや、リストの中から特定の条件に当てはまる要素だけ抽出してリストを作成することが挙げられます。

大きなメリットとしては、簡潔にリストを書くことができることと、同じ操作をfor文で行う場合よりも高速にリストを作成することができる点です。

リスト内包表記は、[式 for 変数 in イテラブル]という形で書きます。イテラブルの各要素を変数として取り出し、でその要素に何かしらの操作を行った上でリストを作成します。

例えば、次のように、1から10までの数字を2乗したリストを生成することができます。

# 1から10までの連続した数字をiとして取り出し、
# i ** 2で数字を2乗してリストに格納する
l = [i ** 2 for i in range(1, 11)]
print(l)
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

最終的にリストの要素となるの部分は、何かの計算や操作をせずにそのまま要素をいれてもOKです。また、タプルや文字列など、他のイテラブルなオブジェクトもリスト内包表記で使うことができます。

イテラブルに関する解説は以下の記事などを参考にしてください。

# 1から10までの連続した数字をiとして取りだし、
# そのままリストに格納する
l = [i for i in range(1, 11)]
print(l)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


# タプルや文字列のようなイテラブルも利用できる
t = (1, 2, 3)
t_list = [i **2 for i in t]
print(t_list)
# [1, 4, 9]

s = 'Hello'
s_list = [i for i in s]
print(s_list)
# ['H', 'e', 'l', 'l', 'o']

リスト内包表記は処理速度が速い

リスト内包表記で行なっていることは、Pythonのfor文でも同じことを実行することができます。

例えば、先ほどの『1から10までの数字を2乗したリストを生成する』という操作をfor文で書くと以下のようになります。

# 空のリストを作成
l = []

# for文でそれぞれの要素にアクセスし、
# 2乗してリストに追加
for i in range(1, 11):
    l.append(i ** 2)

print(l)
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

リスト内包表記の方が、1行でスマートに記述することができるというメリットがあるのですが、実行速度の面でもfor文よりもメリットがあります。

for文の場合、ループごとにリストのappend()メソッドを呼び出すため、追加の参照コストがかかってしまうなどの理由で、処理が遅くなってしまいます。

上記の処理を1000万回分に増やして、for文とリスト内包表記の処理速度を比較してみましょう。

import time

# リスト内包表記
def list_comprehension():
    start = time.time()
    l = [i ** 2 for i in range(10000000)]
    end = time.time()
    print(f"リスト内包表記の実行速度:{end-start}秒")


# for文
def for_loop():
    start = time.time()
    l = []
    for i in range(10000000):
        l.append(i ** 2)
    end = time.time()
    print(f"for文の実行速度:{end-start}秒")


list_comprehension()
# リスト内包表記の実行速度:2.516500949859619秒

for_loop()
# for文の実行速度:2.7185871601104736秒

わずかですが、for文の処理よりもリスト内包表記の処理の方が速いことがわかりました。私の環境ではこのくらいの差でしたが、実際の実行環境によってはより差がつく場合もあるため、同じ処理を書くのであればなるべくリスト内包表記を使う方が良いでしょう

リスト内包表記とif文を組み合わせる方法

リスト内包表記は、リストのようなイテラブルの各要素から、特定の条件に合ったものだけ抽出して操作することができます。この場合、リスト内包表記の中でifステートメントを利用します。

表記方法は、[式 for 変数 in イテラブル if 条件式]となり、イテラブルから取り出した変数(要素)が条件式に当てはまれば、式の中で操作を行ってリストに格納します

『1から10までの数字を2乗したリストを生成する』という操作に条件を加えて『1から10までの奇数の数字を2乗したリストを生成する』という例をみてみましょう。

# 1から10までの連続した数字をiとして取り出し、
# iが奇数であれば、
# i ** 2で数字を2乗してリストに格納する
l = [i ** 2 for i in range(1, 11) if i % 2 != 0]
print(l)
# [1, 9, 25, 49, 81]

リスト内包表記とif-else文を組み合わせる方法

リスト内包表記はif文だけではなく、if-else文の記法も使うことができます。

表記方法は、[式1 if 条件 else 式2 for 変数 in イテラブル]となり、ifだけを使った表記方法とは条件式を書く位置が異なるので注意してください条件に当てはまる場合(if式1、当てはまらない場合(else式2をリストの要素として格納します。

以下の例では、『1から10までの数字で、奇数であれば数字を2乗したもの、偶数であれば"偶数"という文字列を格納するリストを生成する』という操作をしています。

# 1から10までの数字で、
# iが奇数であれば数字を2乗したもの、
# iが偶数であれば"偶数"という文字列を格納する
l = [i ** 2 if i % 2 != 0 else "偶数" for i in range(1, 11)]
print(l)
# [1, '偶数', 9, '偶数', 25, '偶数', 49, '偶数', 81, '偶数']

ネストしたリスト内包表記の使い方(二重のリスト)

リスト内包表記はネストさせることができます。リストの中にリストなので、いわゆるマトリックス(二次元リスト)ですが、基本的な扱い方は同じです。

例えば、九九の計算を表現するマトリックス(二次元リスト)を作成することを考えましょう。まずはfor文による作成方法をみてみます。

# 1から9までの数字の羅列
nums = range(1, 10)

# 九九の結果を格納するリスト
kuku_list = []

for i in nums:
    # それぞれの九九の段の結果を格納するリスト
    dan_list = []
    for j in nums:
        result = i * j
        dan_list.append(result)
    kuku_list.append(dan_list)

print(kuku_list)
"""
[[1, 2, 3, 4, 5, 6, 7, 8, 9],
 [2, 4, 6, 8, 10, 12, 14, 16, 18],
 [3, 6, 9, 12, 15, 18, 21, 24, 27],
 [4, 8, 12, 16, 20, 24, 28, 32, 36],
 [5, 10, 15, 20, 25, 30, 35, 40, 45],
 [6, 12, 18, 24, 30, 36, 42, 48, 54],
 [7, 14, 21, 28, 35, 42, 49, 56, 63],
 [8, 16, 24, 32, 40, 48, 56, 64, 72],
 [9, 18, 27, 36, 45, 54, 63, 72, 81]]
"""

for文を使う操作では、リストとfor文を二つ作成し、それぞれのループの中で必要な操作を行なっています。これをリスト内包表記に置き換えると、1行ですっきり書くことができます。

# リスト内包表記をネストさせる
kuku_list = [[i * j for j in range(1, 10)] for i in range(1, 10)]

print(kuku_list)
"""
[[1, 2, 3, 4, 5, 6, 7, 8, 9],
 [2, 4, 6, 8, 10, 12, 14, 16, 18],
 [3, 6, 9, 12, 15, 18, 21, 24, 27],
 [4, 8, 12, 16, 20, 24, 28, 32, 36],
 [5, 10, 15, 20, 25, 30, 35, 40, 45],
 [6, 12, 18, 24, 30, 36, 42, 48, 54],
 [7, 14, 21, 28, 35, 42, 49, 56, 63],
 [8, 16, 24, 32, 40, 48, 56, 64, 72],
 [9, 18, 27, 36, 45, 54, 63, 72, 81]]
"""

ネストさせたリスト内包表記の書き方は、[[式 for 変数2 in イテラブル2] for 変数1 in イテラブル1]という形になります。の部分では変数1にもアクセスすることができ、計算に利用することができます。

ネストされたリストから要素を取り出してリスト内包表記を使う方法

今後は逆に、ネストされたリスト(マトリックス)から一つ一つの要素を取り出して処理する方法を解説します。リスト内包表記では、内部でfor文を重ねることができます。

for文を2回重ねる基本構文は、[式 for 変数1 in イテラブル for 変数2 in 変数1]という形です。少し複雑ですが、この場合、まずイテラブルから変数1(これもイテラブル)を取り出して、その変数1の中の要素を二つ目のfor文で変数2として取り出します。最後に、変数2の要素を式のなかで操作して一つのリストを作成します。

先ほど作成した九九の二次元リストから要素を取り出して、一つのリストに並べる操作をしてみましょう。

# 2次元のリストを作成
kuku_list = [[i * j for j in range(1, 10)] for i in range(1, 10)]

print(kuku_list)
"""
[[1, 2, 3, 4, 5, 6, 7, 8, 9],
 [2, 4, 6, 8, 10, 12, 14, 16, 18],
 [3, 6, 9, 12, 15, 18, 21, 24, 27],
 [4, 8, 12, 16, 20, 24, 28, 32, 36],
 [5, 10, 15, 20, 25, 30, 35, 40, 45],
 [6, 12, 18, 24, 30, 36, 42, 48, 54],
 [7, 14, 21, 28, 35, 42, 49, 56, 63],
 [8, 16, 24, 32, 40, 48, 56, 64, 72],
 [9, 18, 27, 36, 45, 54, 63, 72, 81]]
"""

# 1次元のリストに展開
# kuku_listからそれぞれの行をdanとして取得し、
# それぞれのdanから各数字をnumとして取得
nums = [num for dan in kuku_list for num in dan]
print(nums)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 4, ... 64, 72, 9, 18, 27, 36, 45, 54, 63, 72, 81]

ぱっと見では複雑な処理に見えますが、基本的な構文がわかればとてもシンプルに処理を記述することができます。