⛏️

【Python】内包記法でリストの抽出を1行で

2022/12/28に公開

要素の抽出にappendはちょっとダサい

要素から、特定のものだけ抽出したいな~と思うこと、ありますよね。
(もしくは、ちょっとしたフィルタリングとか。)
でも、各要素にアクセスして分類するのはちょっと面倒・・・。
試しに、適当に生成したリストからifforを使って、自然数だけを抽出してみようと思います。

values = [5, -26, 49, -92, -3, 10, 97]
result = []
for v in values:
    if 0 < v:
        result.append(v)
print(result)
# [5, 49, 10, 97]

うん・・・。
内容を知るためだけに4行も読まなきゃいけないのは、わずらわしすぎます。

そんな時に使えるのが、内包記法
エレガントに一行で抽出ができちゃいます。

内包記法

内包記法を使うと、次のように記述することができます。

values = [5, -26, 49, -92, -3, 10, 97]
result = [ v for v in values if 0 < v ]
print(result)
# [5, 49, 10, 97]

なんだこれ?と思ったアナタに、この記事はピッタリ。
for, if, appendといった処理がまとめて1行になってしまいました。
なんてスッキリしたコードでしょうか。

なぜこう書けるのか

では内包記法がどうなっているかをサックリ説明してみます。
内包記法の式は、次のような順番になっています。

result = [ <結果となる式> <forループ> <ifでの分岐> ]

forループは、for v in valuesの部分です。
通常のfor文のように各要素に何かしたいものが入っているリストを指定します。
通常のforと同じです。

ifでの分岐は、if 0 < vの部分です。
通常のif同様に条件を指定します。
Trueでない要素の場合、スキップされます。
今回は0 < vなので、forから受け取った要素が0より大きいものだけ処理が行われます。

結果となる式は、vの部分です。
条件がTrueだった時に要素となる式を指定します。
今回はvなので、何もせずに要素がそのままリストに入ります。

そして最後の外側の[]によって結果がリスト化され、resultに代入されます。

elseも欲しい場合

仮に要素が0未満だった場合の結果を0にしてみましょう。
この場合は、elseで0を入れればいいですね。

# エラーとなる式
result = [ v for v in values if 0 < v else 0 ]

# 正しい式
result = [ v if 0 < v else 0 for v in values ]

ここで気を付けるべき点は、if-elseforの手前に来ることです。
なんで???

なぜこう書けるのか

先ほどの構図を思い出してみましょう。
こんなのでしたね。

result = [ <結果となる式> <forループ> <ifでの分岐> ]

この構図は、実は変わっていません。

でも、よく見てください。
ifでの分岐なので、if-elseでの分岐ではないんです。
つまりelseには対応していません。
(一休さんかな?)

じゃあ、どうすればよいか・・・。
ifでの分岐は、分岐する必要がなければ、省略可能です。
なので、そこを省略してしまいましょう。
(全ての要素がTrueになります。)

result = [ <結果となる式> <forループ> ]

ここで結果となる式三項演算子を使えばよいのです。
(三項演算子の復習は、各自でお願いいたします。)
これが、if-elseforの前に来る原因です。
あとは、この三項演算子の結果がすべての要素になります。

余った<ifでの分岐>は使えないの?

使えます。

実は、三項演算子によって「要素を代入しない」という処理は実現できません。
結果となる式を使うことで、これを実現することができます。

たとえば、上のコードをさらに「100以上なら含めない」という条件を付け足します。
100より大きい要素も付け足しました。

values = [5, -26, 49, -92, -3, 10, 97, 200, 5000, 10000]
result = [ v if 0 < v else 0 for v in values if v < 100 ]
print(result)
# [5, 0, 49, 0, 0, 10, 97]

結果となる式は、v if 0 < v else 0です。
0以上であれば値をそのまま結果とし、0以下であれば0とします。

forループは、for v in valuesです。
これは変わりませんね。

ifでの分岐は、if v < 100です。
これで、100未満の値しか要素として残りません
結果に200500010000がいませんね。

これであなたもイカれ文法(内包記法)マスター

習得すれば普通に使えますが、これほかの言語ユーザから見たらどう見てもイカれ文法ですよね。
私もそう呼んでます。
でも好きです。
あふれ出る変態感。

それではよいPythonライフを。

参考

Discussion