🐍
Effective Python 読書メモ
概要
Effective Pythonを読んだ上での要点や、使えそうな知識をメモとして記す
対象本
効率的で堅牢であるだけでなく、読みやすく、保守しやすく、改善しやすいPythonicなコードを書く秘訣を教えます。
1章 Pythonic思考
項目2 PEP8スタイルガイドに従う
PEP8 : https://peps.python.org/pep-0008/
正しい構文である限り、コードは自由に記述して良いが、一貫したスタイルに従い記述することでより扱いやすく可読性を向上させる。
-
空白(スペース)
- インデントにはタブではなくスペースを使う
- 各行は長さが79文字以下にする
- ファイル内では、関数とクラスは、空白2行で分ける
- クラスでは、メソッドは空白行で分ける
- 辞書では、キーとコロンの間に空白を置かず、同じ行に値を書く場合には値の前に空白を1つおく
- 変数代入の前後には、空白を1つ、必ず1つだけ置く
- タイプヒントでは、変数名の直後にコロンを置き、型情報の前に空白を1つだけ置く
number: int # 変数名の直後にコロン、型情報の前にスペース def hoge(): pass class Point: # 上の関数から2行空ける coords: Tuple[int, int] label: str = '<unknown>'
-
命名に関して
- 関数、変数、属性は、
lowercase_underscore
のように小文字で_
を挟む - プロテクテッド属性は
_leaing_underscore
のように_
を先頭につける - プライベート属性は
__double_underscore
のようにのように_
を2つ先頭につける - クラス・例外に関しては、CapitalizedWordのように先頭を大文字にする
- モジュールでの定数は、
ALL_CAPS
のように全て大文字で_
を挟む - クラスのインスタンスメソッド(オブジェクトを参照する)は第一引数にselfを使う
- クラスメソッドは(クラスを参照する)第一引数にclsを使う
- 関数、変数、属性は、
-
式と文
- 式の否定ではなく、内側の項の否定を使う
- コンテナやシーケンスの長さを使って、空の値の評価を行わない。暗黙にFalseと評価されることを使う
- 上記と同様に、空の値でない時の評価も暗黙にTrueとなることを利用する。
# 式の否定を使わない if not fruit is apple: # NG if fruit is not apple: # OK # len()を用いて空判定を行わない if len(fruit_list) == 0: # NG if not fruit_list: # OK # 空でない時にTrueとなることを利用する if len(fruit_list) != 0: # NG if fruit_list: # OK
項目6 インデックスではなく複数代入アンパックを使う
- pythonには1つの代入文で複数の値を代入できるアンパック構文がある
- アンパックを利用することで、コードの意図が明確になり、簡潔に記述できる
# インデックスを利用する def bubble_sort(words): for _ in range(len(words)): for i in range(1, len(words)): if words[i] < words[i-1]: tmp = words[i] words[i] = words[i-1] words[i-1] = tmp names = ['shine', 'apple', 'carrots', 'great'] bubble_sort(names) print(names) # ['apple', 'carrots', 'great', 'shine'] # アンパックを利用する def bubble_sort_unpack(words): for _ in range(len(words)): for i in range(1, len(words)): if words[i] < words[i-1]: words[i-1], words[i] = words[i], words[i-1] # アンパックを利用して1行でスワップできる names = ['shine', 'apple', 'carrots', 'great'] bubble_sort_unpack(names) print(names) # ['apple', 'carrots', 'great', 'shine']
項目7 rangeではなくenumerateを使う
- enumerateを利用して、イテレータでループしながら、要素のインデックスを取り出すことができる
- enumerateの第2引数でカウンタを開始する数を指定できる
- enumerateは遅延評価ジェネレータである
- 一度に全てを読み込むわけでなく、必要な時に読み込む
- forループ毎に値を生成している
- メモリ効率の上昇や大規模データを扱いやすいという特徴がある
※enumerateのドキュメント
flavor_list = ['vanilla', 'chocolate', 'pecan']
for i in range(len(flavor_list)):
flavor = flavor_list[i]
print(f"[{i + 1}]:{flavor} is delicious")
for index, flavor in enumerate(flavor_list, 1): # 開始数を1として指定
print(f"[{index}]:{flavor} is delicious")
イテレータを並列処理するにはzipを使う
- zipは2つ以上のイテレータを遅延評価ジェネレータでラップする
import itertools
names = ['Ceila', 'Lise', 'Maria']
counts = [len(name) for name in names]
max_count = 0
longest_name = None
# ラップしているイテレータの要素を1つずつ処理する
for name, count in zip(names, counts):
if count > max_count:
longest_name = name
max_count = count
names.append('Rosalind') # 新しく名前を追加
# カウント数の更新をしていないので、最短の入力長3回でループが終わる
for name, count in zip(names, counts):
print(name)
# 全ての要素に対して処理を行いたい場合
for name, count in itertools.zip_longest(names, counts):
print(f"{name} : {count}") # Rosalind : Noneになる
Discussion