nikkie-ftnextの日記

イベントレポートや読書メモを発信

Pythonの内包表記、ifを複数並べて書けた...だと...(初めて知って相当の衝撃)

あ...ありのままに今起こったことを話すぜ!

※今回は特別編です

目次

そもそもことのはじまりは

金曜日のミノ駆動本_読書会(7章 コレクション)に向けて予習1をしていたnikkie氏。
Effective Python 第2版』を読んでいると...

内包表記は、複数のif条件もサポートします。
同一のループでの複数条件は、暗黙のand式となります。(項目28より)

複 数 の if 条 件 だ と ...

ifを複数使った内包表記、動くんです!

Effective Pythonのサンプルコードを参照しましょう。
https://github.com/bslatkin/effectivepython/blob/4ae6f3141291ea137eb29a245bf889dbc8091713/example_code/item_28.py#L79-L86

1〜10の整数の中から4より大きい偶数を取り出すコードです。

>>> # 内包表記にifを複数書ける!!
>>> b = [x for x in range(1, 11) if x > 4 if x % 2 == 0]
>>> b
[6, 8, 10]

>>> # 複数のifはandと同じ
>>> c = [x for x in range(1, 11) if x > 4 and x % 2 == 0]
>>> c
[6, 8, 10]

>>> b == c
True

私、ずっとandを連ねて書いてました〜

言語リファレンスの構文定義を確認

6.2.4. リスト、集合、辞書の表示を確認しました。

BNFの構文定義を読んでみると

  • comp_forの定義でcomp_iterはオプショナル
  • comp_itercomp_forまたはcomp_if
  • comp_if ::= "if" or_test [comp_iter]
    • つまり、comp_ifのあとにcomp_iterであるcomp_ifを続けられる=複数ifを書ける
    • or_testは今回深く追っていませんが、ブール演算や比較など、True/Falseを返すものっぽいという理解です

また、PEG2の説明では

listcomp:
    | '[' named_expression for_if_clauses ']' 
  • for_if_clausesfor_if_clauseの1つ以上の繰り返し
  • for_if_clauseは、雑に言うと、for ... in ... (if ...)*
    • (if ...)*はifの0個以上の繰り返し、つまり複数のifが書ける!

10. 完全な文法仕様Comprehensions & Generatorsを見ています

構文定義でも内包表記の複数ifは明示されていたのですね...

終わりに

Pythonを書き始めて5年ほどだと思いますが、内包表記に複数のifというのは初めて知りました。
過去の私はなにかの書籍やWeb上のリソースで内包表記を身に着けたのだと思いますが、複数ifが書けることは見逃したのか書かれていなかったのか、今回初めて知って相当な衝撃を受けました。

if、お前、内包表記で複数使えたのか...
5 年 ぶ り真実に近づいたぞ...

書ききってみて、if文がネストできることを考えるとようやく自然な気がしてきました