【Effective Python勉強メモ】1章:Python流思考(Pythonic Thinking)


はじめに

こちらはEffective Pythonを読んで個人的に勉強になった項目のメモ書きになります。
書籍の"まえがき"には「項目間は自由に拾い読みしていいよ」と書いてあるのですが、前から順番に読んでみます。
なお、2020/07/26に第二版が発売されているようですが、ここでは2016年に発売された書籍を扱いますのでご了承ください。

Effective Python ―Pythonプログラムを改良する59項目 (日本語) 大型本 – 2016/1/23

1章:Python流思考(Pythonic Thinking)

Pythonプログラマは、明示すること、複雑さよりは単純さを選ぶこと、可読性を最大化することを好みます。
他の言語に馴染んだプログラマは、Pythonをあたかも、C++、Javaあるいは一番よく知っている言語と同じであるかのように書こうとするものです。

後で1章を読み進めてからこの前書きを読み返してみたんですが、知らなかった仕様・表現も出てきたりして、"Python流"を把握できてなかったなと思いました。


項目2:PEP8スタイルガイドに従う

  • 式と文(一部項目を抜粋)
    • 長さを使って(if len(somelist) == 0)空値([]や''など)かどうかをチェックしない。
    • if not somelistを使って、空値が暗黙にFalseと評価されることを使う

うっ、これ結構やっちゃってる気がします。。ごめんなさいもうしません。。


項目5:シーケンスをどのようにスライスするか知っておく

スライスでは、リストの境界を越えた添字のstartとendも適切に扱われます。したがって、入力シーケンスを考慮して最大長を設定したコードもたやすく書けます。

下記は実行可能です。
シーケンス長に関わらず20要素までは取り出すというコードがスライスで簡単に実現できるということですね。

a = ['a', 'b', 'c', 'd', 'e', 'f']
a[:20]

実行結果
['a', 'b', 'c', 'd', 'e', 'f']

あと下記は恥ずかしながら知りませんでした。。

代入するスライスの長さは同じでなくても構いません。代入の前後でスライスの値は保全されています。

試しにやってみます。

a = ['a', 'b', 'c', 'd', 'e', 'f']
a[2:4] = [1, 2, 3, 4, 5]
print(a)

実行結果
['a', 'b', 1, 2, 3, 4, 5, 'e', 'f']

ほんとだ、できた。


項目6:1つのスライスでは、start, end, strideを使わない

# somelist[start:end:stride]
a[2::2]
a[-2::-2]
a[-2:2:-2]
a[2:2:-2]

要点は、スライス構文のstride部分が極端に人を惑わせるということです。(中略)このような問題を避けるには、startやendの添字と一緒にstrideを使わないことです。


項目8:リスト内包表記には、3つ以上の式を避ける

項目7では、mapやfilterの代わりにリスト内包表記を使うことを推奨していますが、式が多くなった場合はリスト内包表記は適切でないとしています。
例えば下記のような場合です。

my_lists = [[[1, 2, 3], [4, 5, 6]]]
flat = [x for sublist1 in my_lists for sublist2 in sublist1 for x in sublist2]

節約した行数は、後の面倒さを納得させられるものではありません。

本当にこれですよね。


項目12:forとwhileループの後のelseブロックは使うのを避ける

ループでbreak文が実行されると、実はelseブロックがスキップされます。

知らなかった。。(書こうとしたこともなかったんですけど)

for i in range(3):
    print('loop: %d' % i)
    # ループ内でbreakしない
else:
    print('Else block!')

実行結果
loop: 0
loop: 1
loop: 2
Else block!

for i in range(3):
    print('loop: %d' % i)
    if i == 1:
        # ループ内でbreakする
        break
else:
    print('Else block!')

実行結果
loop: 0
loop: 1

ほんとだ…!
ループの直後のelseブロックは振る舞いが直感的でないから使わないべき、と書いてあります。

まとめ

個人的にぐっときたポイントを書き出してみました。

  • 項目2:PEP8スタイルガイドに従う
    • 長さを使って空値をチェックしない。
  • 項目5:シーケンスをどのようにスライスするか知っておく
    • スライスでは、境界外の添字が許される
    • スライスへの代入は、元のシーケンスの指定範囲の長さが違っていても実行可能
  • 項目6:1つのスライスでは、start, end, strideを使わない
    • スライスでは、できるだけ正のstride値をstartかendのうちどちらか一方のみと一緒に使うようにする
  • 項目8:リスト内包表記には、3つ以上の式を避ける
    • 3つ以上の式を使うリスト内包表記は、読むのが難しく、避けるべきだ。
  • 項目12:forとwhileループの後のelseブロックは使うのを避ける
    • ループの後のelseブロックは、ループ本体でbreak文が実行されなかった場合にのみ実行される。
    • ループの直後のelseブロックは振る舞いが直感的でなく、誤解を生みやすいので使わない。

おわりに

1章から既に思い当たる節が色々あって勉強になりました。
この後もまとめていきたいと思います。
全ての項目をまとめているわけではないので、気になる方は書籍の購入をご検討ください(回し者ではありません)