【 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 文は左から右へと処理されます。
リスト内包表記を利用することで簡潔に、読みやすいコードで記述することができていますね。
複数の for 文を含むリスト内包表記が有効なもう1つの場面は、上で利用したような二次元配列をコピーする操作です。
今回は同時に各セルに含まれる値を2乗してみましょう。
これを for 文を利用した通常の構文で記述すると次のようになるでしょう。
リスト内包表記では [] が2重になってしまっていますが、十分に読解性が保たれている上に、コードがかなり簡潔になっています。
では、三次元配列をターゲットにした場合はどうでしょうか?フラットなリストを作成してみます。
読みやすさを考慮して複数行に分割して記述してみました。
通常の for ループ構文でも記述してみましょう。
リスト内包表記を利用してもそれほどコード量は少なくなっていませんし、読解性も ??? ではないでしょうか。
ちなみに、配列構成はそのままコピーし、各要素だけを2乗する処理では次のようになります。
次数が増加していますから、当然ながら [] の記述量も増えており、ちょっと目にウルサいですね。
それに、このコードをパッと見せられてすぐに解読できる人は少ないと思います。
内包表記中には複数の if 文を含めることもできます。
その場合、同じ for 文のレベルに存在する複数の if 文は暗黙的に and 書式である、とみなされます。
a のリストから 4 より大きい偶数の値だけを取り出してリストを作成しましょう。
b の内包表記も c の内包表記も処理内容は同じです。
if 文はレベルの異なる for 文にそれぞれ記述することもできます。
二次元配列をコピーします。ただし、セル内の要素の合計が 10 以上のセルだけを対象とし、かつ、そのセルの中で 3 で割り切れる要素だけを残します。
どうでしょう?かなり入り組んできましたね。
もちろんこのような記述が最良のものとして選択される場面もあるかもしれませんが、
開発チーム内で多くの人と協働している場合、特に読解性の面からできるだけ避けたほうがいいように思われます。
特に辞書内包表記 (dictionary comprehension) ではただでさえ key、value という2つのパラメータを扱わなければいけませんから、
よりコードがごちゃごちゃになってしまうことは想像に難くありません。
一般的なルールとしては、内包表記中に含める for 文、if 文は併せて2つまで、ということにしておけば問題はないと思います。
例えば、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)
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)
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)
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]]
]
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)
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)
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)
print(c)
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)
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 ... -