検索ガイド -Search Guide-

単語と単語を空白で区切ることで AND 検索になります。
例: python デコレータ ('python' と 'デコレータ' 両方を含む記事を検索します)
単語の前に '-' を付けることで NOT 検索になります。
例: python -デコレータ ('python' は含むが 'デコレータ' は含まない記事を検索します)
" (ダブルクオート) で語句を囲むことで 完全一致検索になります。
例: "python data" 実装 ('python data' と '実装' 両方を含む記事を検索します。'python data 実装' の検索とは異なります。)
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
>>
effective_python

【 Effective Python, 2nd Edition 】内包表記に含める for 文や if 文の数は2つ位までに抑えておかないと読解性が極端に悪くなりますよ、という話 投稿一覧へ戻る

Published 2020年6月25日19:11 by mootaro23

SUPPORT UKRAINE

- Your indifference to the act of cruelty can thrive rogue nations like Russia -

内包表記 (comprehension) には複数の for 文を含めることができます。


例えば、for ステートメントを2つ含んだリスト内包表記で matrix をフラットなリストに変換しようと思います。
同じレベルで並んでいる for 文は左から右へと処理されます。


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


flat = [x for row in matrix for x in row]


print(flat)

# [1, 2, 3, 4, 5, 6, 7, 8, 9]




リスト内包表記を利用することで簡潔に、読みやすいコードで記述することができていますね。


複数の for 文を含むリスト内包表記が有効なもう1つの場面は、上で利用したような二次元配列をコピーする操作です。
今回は同時に各セルに含まれる値を2乗してみましょう。


squared_matrix = [[i**2 for i in row] for row in matrix]


print(squared_matrix)

# [[1, 4, 9], [16, 25, 36], [49, 64, 81]]




これを for 文を利用した通常の構文で記述すると次のようになるでしょう。


squared_matrix = []
for row in matrix:
each_row = []
for i in row:
each_row.append(i**2)
squared_matrix.append(each_row)


print(squared_matrix)

# [[1, 4, 9], [16, 25, 36], [49, 64, 81]]




リスト内包表記では [] が2重になってしまっていますが、十分に読解性が保たれている上に、コードがかなり簡潔になっています。


では、三次元配列をターゲットにした場合はどうでしょうか?フラットなリストを作成してみます。


three_d_matrix = [
[[1, 2, 3], [ 4, 5, 6]],
[[7, 8, 9], [10, 11, 12]]
]


flat = [i for row in three_d_matrix
for i_row in row
for i in i_row]


print(flat)

# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]




読みやすさを考慮して複数行に分割して記述してみました。
通常の for ループ構文でも記述してみましょう。


flat = []
for row in three_d_matrix:
for i_row in row:
flat.extend(i_row)


print(flat)

# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]




リスト内包表記を利用してもそれほどコード量は少なくなっていませんし、読解性も ??? ではないでしょうか。
ちなみに、配列構成はそのままコピーし、各要素だけを2乗する処理では次のようになります。


squared_matrix = [[[i**2 for i in sublist2]
for sublist2 in sublist1]
for sublist1 in three_d_matrix]


print(squared_matrix)

# [[[1, 4, 9], [16, 25, 36]], [[49, 64, 81], [100, 121, 144]]]




次数が増加していますから、当然ながら [] の記述量も増えており、ちょっと目にウルサいですね。
それに、このコードをパッと見せられてすぐに解読できる人は少ないと思います。


内包表記中には複数の if 文を含めることもできます。
その場合、同じ for 文のレベルに存在する複数の if 文は暗黙的に and 書式である、とみなされます。


a のリストから 4 より大きい偶数の値だけを取り出してリストを作成しましょう。
b の内包表記も c の内包表記も処理内容は同じです。


a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


b = [x for x in a if x > 4 if x % 2 == 0]
c = [x for x in a if x > 4 and x % 2 == 0]


print(b)

# [6, 8, 10]




print(c)

# [6, 8, 10]




if 文はレベルの異なる for 文にそれぞれ記述することもできます。
二次元配列をコピーします。ただし、セル内の要素の合計が 10 以上のセルだけを対象とし、かつ、そのセルの中で 3 で割り切れる要素だけを残します。


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


filtered = [[i for i in row if i % 3 == 0]
for row in matrix if sum(row) >= 10]


print(filtered)

# [[6], [9]]




どうでしょう?かなり入り組んできましたね。
もちろんこのような記述が最良のものとして選択される場面もあるかもしれませんが、
開発チーム内で多くの人と協働している場合、特に読解性の面からできるだけ避けたほうがいいように思われます。


特に辞書内包表記 (dictionary comprehension) ではただでさえ key、value という2つのパラメータを扱わなければいけませんから、
よりコードがごちゃごちゃになってしまうことは想像に難くありません。


一般的なルールとしては、内包表記中に含める for 文、if 文は併せて2つまで、ということにしておけば問題はないと思います。
この記事に興味のある方は次の記事にも関心を持っているようです...
- People who read this article may also be interested in following articles ... -
【 Effective Python, 2nd Edition 】assignment expression (walrus operator, :=) を利用して内包表記におけるサブ書式の重複を回避しよう!
【 Effective Python, 2nd Edition 】throw() メソッドを利用したジェネレータ ( generator ) 内部での状態遷移はなるだけ避けましょう。ネストが深くなってコードの読解性が落ちちゃいますよ!
【Python 雑談・雑学】 list comprehension (リスト内包表記) は節度を持って使ってください、という話
【 Effective Python, 2nd Edition 】組み込みタイプ ( built-in types ) を利用していてネストが深くなってきたらクラス ( class ) を作成する頃合いです、の巻
【 Effective Python, 2nd Edition + coding challenge 】プログラム開発のどの段階で並列処理 ( concurrency ) が必要になるのだろう? そのときどのようにリファクタリング ( refactoring ) していけばいいのだろう? を考えてみるシリーズ ( のはず ) 第1回
【 Effective Python, 2nd Edition 】入力元のデータサイズが大きい場合は、リスト内包表記 ( list comprehension ) ではなくジェネレータ式 ( generator expression ) の利用を検討しよう!
【 Effective Python, 2nd Edition 】dictionary comprehension の使い道