Pythonicでいこう
今年 1 月から Python(Django) を使ったバックエンド開発の案件に参画しています。
それまでの Python の実務経験は昨年 2 ヶ月ほど別案件に携わっていた程度で、まだ浅いですが、3 ヶ月間携わる中で、既存コードの書き方にやきもきする機会があり、それを吐き出したくなったので記事にしました。
タイトルにある「Pythonic」とは「Python の哲学やスタイルに沿った、自然で洗練されたコードの書き方」という意味です。すっきりとした無駄のないコードを目指して、 Pythonic を意識していきましょう!
ここでは既存コードと修正後の書き方を比較しながら紹介していきます。
True や False との比較
修正前
if is_active == True:
if
文の中は真偽値として評価されるため、わざわざ == True
と比較することは冗長です。
修正後
if is_active:
False
との比較も同様に書けます。
if not is_active:
for 文使いがち
修正前
even_numbers = []
for i in range(1, 11):
even_numbers.append(i)
空配列を作成して for 文で回し、配列に追加して...と、最低 3 行以上必要になります。これは以下のような「リスト内包表記」として書けます。
修正後
even_numbers = [i for i in range(1, 11)]
処理が複雑になると読みづらくなりますが、この程度であれば1行で書ける「リスト内包表記」をオススメします。
例えば、偶数だけを抽出したい場合は、if
条件も同じ 1 行に書けます。
even_numbers = [i for i in range(1, 11) if i % 2 == 0]
配列の存在チェック
修正前
my_list = [1, 2, 3]
if len(my_list) != 0:
「リストが空でない(要素が1つ以上ある)」ことを確認しているのでしょうが、少し冗長です。
万が一、my_list
が None
の場合、len()
でエラーになってしまいます。
修正後
リストが空や None
の場合だと if
文で False
と評価されるため、こう書くことで安全かつ簡潔に記述できます。
my_list = [1, 2, 3]
if my_list:
無意味な else の使用
修正前
if status == 'success':
return True
else:
return False
修正後
if-else
でそのまま真偽値を返しているだけなら、条件式を直接 return
する方がシンプルで読みやすいです。
return status == 'success'
try-except での冗長な存在チェック
修正前
try:
value = my_dict['key']
except KeyError:
value = None
修正後
value = my_dict.get('key')
.get()
メソッドを利用すればキーが存在しない場合、デフォルト値を返すため、例外処理は不要です。None
がデフォルトなので .get('key', None)
とせずに .get('key')
にしましょう。
なお、0
や ''
、[]
などをデフォルトとして使いたい場合は、第二引数に指定することで対応できます。
count = my_dict.get('count', 0)
items = my_dict.get('items', [])
exclude の中で否定
ここでは Django の ORM で利用できるクエリメソッドを紹介します。.exclude()
は、指定した条件に一致しないレコードを抽出し、.filter()
は指定した条件に一致するレコードを抽出します。
修正前
exclude(~Q(id=user.id)
~Q
は否定を表しています。上記のように .exclude(~Q(...))
としてしまうと「否定の否定」となり、ロジックがわかりづらくなります。
修正後
filter(id=user.id)
否定を使いたい場合は、.filter(~Q(...))
のように記述する方がシンプルで明確です。.exclude()
は否定形をそのまま表現したいときに使用しましょう。
これらが必ずしも正解というわけではありません。
すっきりとした無駄のないコードよりも可読性を優先させたいなど各プロジェクトの方針によって判断は異なります。
今回参画しているプロジェクトではコーディングルールがなく、既存のコードに合わせてほしいというスタイルでした。
解せないコードがあれば随時この記事に追加していきます。