読んだメモ:「Effective Python」
Effective Python(第1版)
特に学びがあった事項だけ雑多にメモ。
1. Python流思考(Pythonic Thinking)
#4
map.get(...)
:- マップから第一引数のキーに対応する値を、キーが存在しない場合は第二引数の値を返す
#5
assert 条件式
で、条件が満たされないときにAssertionErrorを投げる
#7
- 辞書や集合も、リスト内包表記のような書き方で定義できる
#9
- 大きいリストやストリームに対してリスト内包表記のような処理をしたいときはジェネレータ式を使う
- 書き方はほぼ同じ([] -> ())だが、リストではなくイテレータを返す。
- そもそもイテレータ、ジェネレータについての解説
#10
- enumerateは第二引数でカウンタのデフォルト値を指定できる
#12
- for、whileループのあとのelseブロックは使うべきではない
- 便利なのに……
#13
- tryのあとはcatch、finallyだけでなくelseも使える
- catchされる例外が怒らなかったときのみ実行
2. 関数
#14
- 使わない変数を
_
と書く記法がある(慣習?)
#15
#16
- ジェネレータを使おう
- イテレータは
list(itr)
で簡単にリストに変換もできる
#17
#18
*args
で可変個数の位置引数を受け取れる- 「位置引数」:与えるときの位置で区別する引数。<->「キーワード引数」
#20
- キーワード引数に動的な値(現在時刻とか)をデフォルト値として入れたいときは、とりあえずNoneにしておくのがよい
- コード内で
is None
で判定して明示的に入れる(ちゃんとコメントに示す)
- コード内で
#21
- 「キーワード専用引数」を使うことができる
- 関数呼び出し時、キーワード引数は位置引数としても(キーワードを指定しなくても)与えることができるが、バグの原因になりうる
- キーワード専用引数として定義すれば、キーワードを明示的に書くことを強制できる
3. クラスと継承
#22
- collections.namedtupleで、名前付きのタプル(値の変更不可)を作れる
- うまく使うと便利だが、自分でクラス定義したほうがいい場合も
#23
- 関数を引数に与えて振る舞いを制御する仕組みを「hook」という
- sortにキーを制御する関数を渡すとか
- クラスにも
__call__()
を定義するとcallableになってhookとして使えるようになる
#24
- 理解できず
#25
- 親クラスを初期化するには常に
super
を使うべき - ダイヤモンド継承してるときには実行順序に注意(MROで定義される)
#26
- 多重継承はなるべく使わず、mix-inを使うべき
- mix-inについては理解できず
#27
- プライベート属性(
__value
)は基本的に好ましくない- 内部的な変数名を変えて(クラス名を頭につけて)クラス外から同じ名前で呼び出せないようにしているだけ(!)なので、この仕組を理解していれば容易にアクセスできてしまう
- 注意をうながすためにプロテクテッド属性(
_value
)を使う - プライベート属性が役立つのは子クラスで同じ名前の変数が定義されないか警戒するときのみ
#28
- コンテナ型を自分でカスタムして新しいクラスを作りたいときはcollections.abcを継承する
- 定義が必要な特殊メソッドを教えてくれる
- 例えば、シーケンスに添え字でアクセスしたときは内部的には
__getitem__()
メソッドが呼び出されていたりするため
- 自分で一から実装しなくても大丈夫なら、既存のコンテナ型(listやdictなど)を継承すれば十分
4. メタクラスと属性
#29
- 基本的に、メンバーのsetter、getterは実装せず、パブリックにアクセスできるようにするのがpythonic
- 必要な場合は、
@property
デコレータとそれに対応するsetter属性をマイグレートする
#_
5. 並行性と並列性
#_
6. 組み込みモジュール
#42
- デコレータで、ある関数を別の関数で修正する
- 引数、返り値を実行ごとにprintする機能を付け加えるとか
- 関数をwrapする関数を作るためのデコレータとして
@wrap
がある- (内部的に認識される)関数名を変更しないので嬉しい
#46
- 便利な組み込みデータ構造がいろいろあるので活用する
- 両端キュー、順序つき辞書、デフォルト値を指定できる辞書、二分探索など
#47
- 数値の精度を求めるときはDecimalを使う
7. 協働作業(コラボレーション)
#49
- 関数、クラス、モジュールには
"""
で囲ったドキュメントをちゃんと書こう- このドキュメントは
__doc__
メソッドを呼び出すことでプログラム内からも参照できる
- このドキュメントは
#_
#53
- 仮想環境を構成するにはpyvenvを使うのがよい
- python3.4以降なら標準で使える
- 結局ベストなのはどれ?
pip freeze
でrequirements.txtを自動的に作れる
8. 本番運用準備
#55
- デバッグの際には
print(obj)
よりもprint(repr(obj))
の方が、型の違いがはっきりするので適している
#56
- unittestモジュールを使うなどしてちゃんとテストをするべき
- モックを使う場合はunittest.mockモジュールもある
#57
- プログラム中に1行、
import pdb; pdb.set_trase()
を入れると、その部分から直接対話型デバッガを起動できる
#58
- 速度低下の原因を、組み込みのプロファイラを使って調べる
- profileモジュールよりもcProfileモジュールのほうがよい
#59
- tracemallocモジュールを使って、メモリリークの原因を調べる
Effective Python 第2版
第2版を読んだので改めて雑多にメモ。 重複もあるが気にしない。
#10
- 代入式
a:=b
:- 条件式内で代入を行えるので、値のチェックと変数としての利用で処理が重複するのを防げて便利
#26
- 関数のデコレータを書く際は、functools.wraps ヘルパー関数を使う
- こうしないと、docstring などのメタデータが引き継がれないという不具合が生じる
#36
- 組み込みモジュールの itertools にはイテレータ、ジェネレータ関連の便利な関数がたくさんある
- 異なる長さに対応する zip_longest など
#39
- クラスメソッドを使うことで、クラスレベルのポリモーフィズムを実現
#44
- setter や getter を不必要に用意せず、クラス属性に直接アクセスするのが pythonic
- 必要な場合は、@property デコレータと組み合わせて型検査付き setter みたいな機能を作れる
#46
- クラス属性にsetter・getterとしての処理を追加したいときは、基本的には@propertyを使えばよい
- クラス内の複数の属性に対して、setter・getterとして同じ処理を使いまわしたいときは、「ディスクリプタ」となるクラスを定義して使うのがよい
- 実装には結構注意点がある
#47
- オブジェクトの属性へのアクセスや設定を遅延的に(オブジェクト自体を定義した後から?)行いたい場合、以下の仕組みを活用する
- クラスに
__getattr__
が定義されている場合、存在しない属性アクセスが発生した際にこれが呼び出される - クラスに
__getattribute__
が定義されている場合、属性アクセスの際に常にこれが呼び出される - クラスに
__setattr__
が定義されている場合、属性への値の設定時に常にこれが呼び出される
- クラスに
#48
type
を継承した「メタクラス」を定義することで、例えばサブクラスの定義に問題がないかのチェックといったメタな視点での動作をサブクラス定義と同時にさせられる- しかしメタクラスを実際に使った実装は大変なので、同等なチェックを親クラスに
__init_subclass__
を定義することで実現できる
#66
- contextlibの@contextmanagerデコレータを使って、with文を独自に作ることができる
- 一時的にログレベルの閾値を変えるなど
#68
- かっちりしたプログラムでpickleを使うときは、copyregの利用を検討する
- オブジェクトの後方互換性を考慮するなど
#69
- 丸め誤差等も考慮して精度の良い正確な値を扱いたいときはDecimalを使う
- 正確な値が欲しいときは、Decimalをstrで初期化する
#70
- 高速化したいときは、cProfileを使ってプロファイル情報を調べる
- profileモジュールよりもcProfileモジュールのほうがよい
#75
- repr でオブジェクトの型情報等もわかりやすくして print デバッグする
- ちなみに、
f'{var!r}'
はf'{repr(var)}'
と等価
#76
- unittest の TestCase の基本的な使い方について
- subTest を用いて、1 つのテストメソッドの中で複数のケースをまとめてチェックできる
#77
- テストの、モジュールレベルの前後処理を setUpModule, tearDownModule で、メソッドレベルの前後処理を setUp, tearDown で行う
- たとえば TemporaryDirectory モジュールは仮のディレクトリ、ファイルを入出力テストに使う際に便利そう
#80
- プログラム中で pdb モジュールの
breakpoint()
を呼ぶだけで、デバッガを開始できる
#85
__init__.py
と__all__
によってパッケージ定義
#87
- ライブラリ作成時には、そのライブラリ内で共通の例外を表すルート例外クラスを作成し、ライブラリ内で発生させる例外はすべてルート例外を継承するようにするとよい
- これにより、ライブラリ利用者はルート例外を catch すればよくなる