Effective Python – Python上級者になるために知っておくべきこと(書籍紹介)

Python Python

Pythonプログラミングを始めてみたけれど、なかなか初心者の域を脱出できない、そんな悩みを持っている人はたとえプロのエンジニアであってもある程度いることと思います。
ネットでは、こういうときはこうすればいい、みたいな記事はあるものの、Pythonプログラミング全般に関してレベルアップを計るような記事はあまりありません。
そんなあなたにお勧めしたいのが、Brett Slatkin「Effective Python」という本です。

「Effective Python」とは

直訳すると、「効果的なPython」。
Effecitiveシリーズは各言語のものが出版されていまして、この本はPython版です。

その名の通り、効果的なPythonプログラミングの手法が全部で90項目紹介されています。

  • Pythonic思考
  • リストと辞書
  • 関数
  • 内包表記とジェネレータ
  • クラスと継承
  • メタクラスと属性
  • 並行性と並列性
  • 頑健性と性能
  • テストとデバッグ
  • 協働作業(コラボレーション)

以上の各項目について書かれています。

Pythonの入門本ではないので、既にPythonを使ってプログラミングをしている人向けの本です。それも、上記の項目にある通り、クラスや、メタクラスや、並列処理などについてよく知悉している人が、更なる高みを目指すための本です。

筆者はこの本を読んで、知っていたこと、自分で心がけていたこともありましたが、はっとする気づきが多くありました。

「Effective Python」の目次より

それでは、「Effective Python」の内容を一部紹介します。

rangeではなくenumerateを使う

例えば次のようなコードを何気なく書いてしまうかもしれません。

datas = ['a', 'b', 'c']
for i in range(len(datas)):
    print str(i) + ':' + datas[i]

リストの繰り返し処理において、リストの内容も欲しいけどインデックスも欲しい、というのは、よく遭遇する場面です。
両方を得るために、このようなコードを書く人は多いでしょう。

ところがこのコードだと、datas[i]へのアクセスが冗長です。両方を一気に得るにはどうすればいいのでしょうか。

このコードは、enumerateというキーワードを使って書き直せます。

datas = ['a', 'b', 'c']
for i,data in enumerate(datas):
    print str(i) + ':' + data

enumerateは、インデックスと内容を同時に取得する手法です。コードが見やすくなり、直感的にも分かりやすくなるので、この場合にはenumerateを使用するようにしましょう。

Noneを返すのではなく例外を送出する

例えば以下のような関数があるとします。

def devide(a, b):
    try:
        return a/b
    except ZeroDivisionError:
        return None

この関数を使用するコードは、以下のようになるでしょう。

a = 1
b = 2
r = devide(a, b)
if r == None:
    print('ゼロで除算されました')
else:
    print(r)

これは非効率です。なんのためにZeroDivisionErrorが定義されているのか分かりません。関数内でエラーを送出したくないからNoneを返すようにしたのでしょうが、呼び出し元で戻り値がNoneかどうかのチェックをいちいちしなくてはならず、二度手間です。さらに、Noneの意味が判然としません。

この関数はこう変えるべきです。

def devide(a, b):
    try:
        return a/b
    except ZeroDivisionError:
        raise ValueError('ゼロで除算しています')

すると呼び出す側はこうなります。

a = 1
b = 2
try:
    print(devide(a,b))
except ValueError:
    print('ゼロで除算されました')

Pythonにはtry/except機構が用意されています。関数内部でエラーが起きて、それが致命的なエラーだった場合、エラーを投げてください。その方が効率的なコードで済みます。用意されている機構は使いましょう。

ローカルクロックにはtimeではなくdatetimeを使う

Pythonで時間を計算するモジュールとして、timeとdatetimeがあります。
どちらも値を文字列に変換できたりします。

ところが、両者の間には以下の違いがあります。

・timeモジュール…OSのローカルタイムに依存する。
・datetimeモジュール…OSのローカルタイムに依存しない。

この二つの違いは決定的です。あなたが開発したシステムが違うタイムゾーンでも動作するようにするためには、timeを使って時間の計算をしてはいけません。

例えば筆者のマシンでは、以下のようになりました。

>>> import time
>>> time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
'2021-02-07 14:37:34'
>>> import datetime
>>> datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
'2021-02-07 14:40:46'

これはたまたま日本語版Windowsだから動作しているだけです。
繰り返しになりますが、違うタイムゾーンで動かす場合、必ずdatetimeモジュールを使用してください。
timeモジュールを使用するのは、time.sleep()のみにとどめておいた方が無難です。

まとめ・より効果的なPythonプログラミングとは

この記事では「Effective Python」の中から3項目を紹介しました。
このように、「Effective Python」には効果的なPythonプログラミングのコツが大量に収められています。

知識として知っていないと実践できない項目もありますが、Noneを返さないなどのように、基本的な知識だけで、考えれば納得できるものもあります。

そしてプログラミングの上級者とは、考えれば納得できるコードを書いている人のことなのです。それが「美しいコード」です。
「美しいコード」は誰もが分かりやすく、協働作業を簡単にします。
プログラミング上級者になりたかったら、考えに考えてコーディングしましょう。初めは大変かもしれませんが、そのうち考えることが習慣になるはずです。習慣化してしまえば大して苦痛は感じません。

同時に、知識のブラッシュアップは常に図りましょう。

あなたがPythonの上級者になれますように。