[解決!Python]リストの内包表記と「if」を組み合わせるには解決!Python

リストの内包表記の中でif句と条件式(if式)を利用して、より柔軟な形で新たなリストの要素を計算する方法を見る。if句と代入式を組み合わせる方法も紹介。

» 2023年11月01日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「解決!Python」のインデックス

連載目次

* 本稿は2021年7月6日に公開された記事をPython 3.12.0で動作確認したものです(確認日:2023年11月1日)。


# if句を使用して、要素を計算する前にフィルタリングをする
r = range(11)
odd_list = [x for x in r if x % 2]
print(odd_list)  # [1, 3, 5, 7, 9]

even_list = [x for x in r if x % 2 == 0]
# even_list = [x for x in r if x not in odd_list]
print(even_list)  # [0, 2, 4, 6, 8, 10]

# 内包表記の要素を決定する式で条件式(三項演算子)を使う
odd_even_list = [(x, 'odd') if x % 2 else (x, 'even') for x in r]
print(odd_even_list)
# 出力結果
#[(0, 'even'), (1, 'odd'), (2, 'even'), (3, 'odd'), (4, 'even'), (5, 'odd'),
# (6, 'even'), (7, 'odd'), (8, 'even'), (9, 'odd'), (10, 'even')]

# if句と代入式の組み合わせ
numlist = [0.06, 0.85, 0.96, 0.79, -0.73, 0.30, -0.81, -0.57, 0.56, 0.32]

def f(x):
    return x + 0.5  # ここでは「何らかの処理」=0.5を加える

numlist2 = [(x, y) for x in numlist if (y := f(x)) > 1]
print(numlist2)  # [(0.85, 1.35), (0.96, 1.46), (0.79, 1.29), (0.56, 1.06)]


if句を使用して、要素を計算する前にフィルタリングをする

 「[解決!Python]内包表記でリストを作成するには」では、内包表記を使ってリストを作成する基本的なパターンや、for句(for節ともいう)をネストさせるパターンなどを紹介した。しかし、内包表記では「if句」(if節ともいう)を使ったり、条件式(if式、三項演算子)を組み合わせたりすることで、より柔軟な形で要素の値を計算できるようになる。

 まずはリストの内包表記のif句から見ていこう。以下はその基本構文だ。

some_var = [expression for item in iterable_obj if condition]


 これは要素の値を計算する前に、「if condition」に記述した条件(condition)が真になるかどうかをテストして、その結果が真の場合にのみ、「expression」が評価されて、その結果が新たなリストの要素となることを意味する。通常、「condition」は「iterable_obj」から「item」に反復的に代入される値を使って表される。このとき、elseに対応する句は存在しない点には注意すること。

 これと同じことをするコードは次のようになる。

some_var = []
for item in iterable_obj:
    if condition:
        some_var.append(expression)


 if句の使用例を以下に示す。

r = range(11)
odd_list = [x for x in r if x % 2]
print(odd_list)  # [1, 3, 5, 7, 9]

even_list = [x for x in r if x % 2 == 0]
# even_list = [x for x in r if x not in odd_list]
print(even_list)  # [0, 2, 4, 6, 8, 10]


 最初の例では、反復可能オブジェクトrから取り出した値は変数xに代入され、「expression」として記述した式「x」が評価される前に、if句で「x % 2」が真であるかどうかがテストされる。この式は「xを2で整数除算した余りを求める」ものなので、その値は0か1となり、結果が1のときは真と見なされるので、余りが1=xが奇数のときには「expression」に書いた式「x」が評価されることになる。つまり、「0〜10の値のうち、奇数を要素とするリスト」が作成される。

 2つ目の例は、その逆で「0〜10の値のうち、偶数を要素とするリスト」を得るものだ。コメントに記したように、条件は数値を計算するものである必要はなく、Pythonで真偽値として扱えるものが得られるのであれば、どのようなものでもよい。

 既に述べたが、注意が必要なのは、if文やif式では条件が成立しなかったときの振る舞いを記述できるが、if句ではこれは不可能であることだ。if句は反復可能オブジェクトから得た値を新たな要素の値の計算に使うかどうかを判断する(フィルタリングする)のであって、反復可能オブジェクトから得た値に応じて新たな要素の値を計算する方法を切り替える(処理を分岐する)ためのものではないと考えた方がよいだろう。後者であれば、この後で説明する条件式を使用する。

 内包表記のfor句と同様に、if句も複数記述できる。以下に例を示す。

values = [(x, y) for x in range(5) if x % 2 for y in range(5) if not y % 2]
print(values)  # [(1, 0), (1, 2), (1, 4), (3, 0), (3, 2), (3, 4)]


 最初のfor句に続くif句の条件は「xの値が奇数なら」を、次のfor句に続くif句の条件は「yの値が偶数なら」を意味するので、この結果は「1と3」と「0と2と4」を組み合わせたタプルを要素とするリストになる。これをfor文とif文を組み合わせたコードに書き下すと次のようになる。

values = []
for x in range(5):
    if x % 2:
        for y in range(5):
            if not y % 2:
                values.append((x, y))

print(values)


 for句とif句は必要なだけ続けられるが、コードが難解になることがないように注意しよう。

内包表記の要素を決定する式で条件式(if式、三項演算子)を使う

 条件式(if式、三項演算子)は、if文とは異なり、式を記述可能な部分で条件分岐を行うのに使える。これを使うと、反復可能オブジェクトから得た値に応じて、新たな値の計算方法を切り替えられる。以下に例を示す。

r = range(11)

odd_even_list = [(x, 'odd') if x % 2 else (x, 'even') for x in r]
print(odd_even_list)
# 出力結果
#[(0, 'even'), (1, 'odd'), (2, 'even'), (3, 'odd'), (4, 'even'), (5, 'odd'),
# (6, 'even'), (7, 'odd'), (8, 'even'), (9, 'odd'), (10, 'even')]


 このコードでは、「expression」の部分で条件式を使用して、xが奇数か偶数かでその計算方法(ここでは元の値と'even'か'odd'の文字列を組み合わせたタプルを生成する)を切り替えている。

 条件式はネスト可能なので、次のような記述も可能だ。

fizzbuzz = ['fizzbuzz' if x % 15 == 0 else 'fizz' if x % 3 ==0 else \
            'buzz' if x % 5 == 0 else str(x) for x in range(1, 16)]
print(fizzbuzz)
# 出力結果
#['1', '2', 'fizz', '4', 'buzz', 'fizz', '7', '8', 'fizz', 'buzz', '11',
# 'fizz', '13', '14', 'fizzbuzz']


 ネストは可能だが、上のコードのようにネストさせすぎると、1行が長くなるのでバックスラッシュ「\」を使った明示的な行継続が必要になったり、コードがそもそも読みにくくなったりするので、使いすぎには注意すること。

if句と代入式の組み合わせ

 最後にif句とPython 3.8で導入された代入式の組み合わせを紹介しておく。代入式は、その名から分かるように、何らかの計算を行った結果を変数に代入しながら式の結果としてその値も返すような式のこと。代入文とは異なり、式を書ける部分に記述できる。代入文とは異なるので、代入式では「変数 := 式」という形で記述する(ただし、あいまいさを取り除くために、かっこで囲む必要がある場合がある)。

 これを利用することで、if句で何らかの計算をした結果を、新たな値の計算に利用できる。例えば、何らかの浮動小数点数値を要素とするリストがあり、それらの値に何らかの処理を加えた結果が1より大きいものだけを取り出して、元の値と処理後の値からなるタプルを要素とするリストがほしいものとしよう。

 このコードを内包表記を使わずに書くと次のようになる。

numlist = [0.06, 0.85, 0.96, 0.79, -0.73, 0.30, -0.81, -0.57, 0.56, 0.32]

def f(x):
    return x + 0.5  # ここでは「何らかの処理」=0.5を加える

numlist2 = [(x, f(x)) for x in numlist if f(x) > 1]
print(numlist2)  # [(0.85, 1.35), (0.96, 1.46), (0.79, 1.29), (0.56, 1.06)]


 もちろん、このように書けば、所定の目標は達成できる。ただ、f関数を2回呼び出しているのが気になる。if句で既に行っている計算をもう一度、行っているということだ。ここでは単純な処理なので問題はないが、f関数では実行に時間がかかる処理をしていたとしたらどうだろう。この計算結果を変数に代入しておいて、新しい要素の計算で使えたら無駄がない。このようなときに代入式が使える。

 代入式を使うように変更したコードを以下に示す。

numlist3 = [(x, y) for x in numlist if (y := f(x)) > 1]
print(numlist3)  # 上に同じ


 このようにすることで、f関数を呼び出した結果を変数yに代入しておいて、要素の計算では「(x, y)」としてその値を利用できている。あいまいさをなくすために、if句では「(y := f(x))」と代入式として評価すべき部分をかっこで囲んでいる点には注意されたい。

「解決!Python」のインデックス

解決!Python

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

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

メールマガジン登録

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