目次
Contents
第4章 良い名前を選ぶ
定数: P.154
短くするよりも意図をわかりやすく表現するほうが大切
命名規則と使用例: P.155
デフォルト値や初期値を設定するためにも使用される
ユーザーが必要な設定をすべて行わなくてもライブラリが動作する、という設計のほうが扱いやすい
設定として使用する際の良いプラクティスは、パッケージ内の1つのファイルにすべての定数を集めること (Django の settings とか)
-
モジュール内にグループとなる定数がある場合を除いて、定数名の先頭に共通の名前をつける必要はない
Python ではモジュール名そのものが接頭辞としての役割を果たすから
組み込みの https://docs.python.org/ja/3/library/enum.html#enum.Enum を使う
辞書型に明示的な名前をつける: P.162
# 例えば dict が名前をキーにしてその人の住所を保持する場合には ``person_address`` # これずっと悩んでた!! person_address = { 'Bill': '6565 Monty Road', 'Pamela': '45 Python street', }
モジュール名とパッケージ名: P.170
モジュールやパッケージの名前は、中に含まれる関数やクラスが持っている目的が伝わるような名前にする
-
underscores のない lowercase 形式の短い名前にしましょう
sqlite
postgres
sha1
第2章 構文ベストプラクティス — クラス以外
Pythonic なパターン: P.64
自分自身で真実を深く掘り下げていくことでしか、Pythonの人気のある書き方のどれが本当に正しいかを知ることはできません。
文字列の結合について: P.67
場合に応じて使い分ける
- s += substring
-
文字列の長さに比例した実行コスト
- str.join()
-
結合したい文字列の数が多い、すでに
iterable
に格納されている - str.format()
-
事前に文字列数がわかっている
- %演算子
-
事前に文字列数がわかっている
- リテラル文字列補完
-
事前に文字列数がわかっている
Pythonのリスト: P.69
Javaとかの
LinkedList
を使って実装されていると勘違いされがちだけど違うよーCPython のリストは可変長の配列として実装されている。
Python のリストは他のオブジェクトへの参照を持った、連続した配列
リストの先頭の構造体がこの配列へのポインタと長さを格納している
-
計算量
- 追加、取得
-
O(1)
- 挿入、削除
-
O(n)
- サイズ変更、再割り当てが発生する操作
-
O(n)
要素の先頭と末尾への append と pop が O(1) の計算量のリンクリストが必要な場合は、
collections.deque
を使用するとよい。
リスト内包表記: P.70
for ループ内で list.appned()
するような場合は、リスト内包表記を使用したほうが処理が速くなる。
# これは遅くなる # リストを操作するコードをループごとにインタープリタ上で処理する必要がある # append() はリストのメソッドであるため、イテレーションごとに関数ルックアップの追加のコストが必要になる events = [] for i in range(10): if i % 2 == 0: events.append(i)
# これは速くなる # 処理の一部がインタープリタ内部で実行されるようになるので、速くなるとのこと [i for i in range(10) if i % 2 == 0]
辞書: P.73
-
辞書内包表記
squares = {number: number**2 for number in range(100)}
辞書内包表記には、リスト内包表記と同じメリットがある。
-
keys()
とか、 Python2 と Python3 では返ってくるもの違うので注意Python2: リスト
Python3: ビューオブジェクト
-
ビューオブジェクトは辞書の内容のスナップショットではなく、現在の内容を見せるビューを提供する。辞書の内容が変化するとビューはその変化を反映した結果を返す。
Python2 と Python3 とで動きが結構違う感じするので、使うとき注意だなぁ・・・
辞書のコピーとイテレーションにおける最悪のケースの計算量の n の数値は、現在格納されている要素数ではなく、辞書が今まで格納してきた最大数とのこと
-
平均計算量
- 要素取得
-
O(1)
- 要素追加
-
O(1)
- 要素削除
-
O(1)
- コピー
-
O(n)
- イテレーション
-
O(n)
以前に大量の要素を格納し、その後要素を減らして現在の要素数がすごく少ない辞書でも、イテレーションすると極めて長い処理時間がかかる!
何度もイテレーションされる辞書の場合は、要素を削除する代わりに新しい辞書オブジェクトを作る方が良いこともある。
-
順序
Python3.7より前までは、順序を保持しない
Python3.7 以降は、辞書のキーが登録した順序で保持される
辞書の実装詳細: P.74
hashable オブジェクトのみがキーとして使える。
オブジェクトが hashable であるということは、オブジェクトが生存する期間中ハッシュ値が変わらず、他のオブジェクトと比較が行えるということ。
Python の組み込み型のうち、 immutable なものはすべて hashable ですと。
-
型が hashable な場合には、次の2つのメソッドを持つプロトコルをサポートすべきであると決められています。
__hash__
__eq__
集合: P.77
-
set()
mutable
順序がない有限集合
要素はユニークかつ immutable かつ hashable
空の集合を作るときは
set()
setリテラルは
{1, 2, 3}
-
frozenset()
immutable かつ hashable
順序がない有限集合
要素はユニークかつ immutable かつ hashable
集合の実装詳細: P.78
CPython実装は辞書に似ている
-
要素の削除、追加、存在チェックは非常に高速
- 平均計算量
-
O(1)
- 最悪計算量
-
O(n)
cllections モジュール: P.78
- namedtuple()
-
タプルのサブクラスを作成するファクトリ関数。名前つきの属性としても要素にアクセスできる。
- deque
スタックとキューに必要な操作を備えた両端キュー。
先頭と末尾への高速な追加、削除ができる
- ChainMap
辞書のようなクラス
複数の辞書をまとめて1つの辞書に見せるビューを作成する。
- OrderedDict
-
要素が追加された順序を保証する辞書のサブクラス
- defaultdict
-
要素が見つからなかったときに、指定された関数を呼び出して初期値を自動作成する辞書のサブクラス
イテレータ: P.79
イテレータプロトコルを実装したコンテナオブジェクト
-
イテレータプロトコル
__next__()
: コンテナの次の要素を返す__iter__()
: イテレータ自身を返す
シーケンスの要素をすべて取り出し終わると
StopIteration
例外が発生する。カスタムイテレータを作成するときは、クラス内に↑のふたつを実装する。
itertools: 使ってください、だそうです。
yield文(ジェネレータ): P.81
関数を一時的に停止させ、途中経過の結果を返す。
一時停止中も実行コンテキストが保存されているため、必要であれば止まった場所から再実行できる
next()
関数呼び出し、あるいは for ループを使って、イテレータと同じようにジェネレータから新しい値を取得できる-
ループ処理やシーケンスを返す関数を実装するときには、まずジェネレータの利用を検討すべき
1つずつ要素を返すことで、その要素を使用する他の関数へ渡す場合に全体のパフォーマンスを向上させる。
-
複数のデータ群を使用するような、データ変換アルゴリズムの効率が向上する。
それぞれのデータ群を1つのイテレータとして実装し、高レベル関数の中にそれらを組み込むことで、巨大で読みにくい関数にになるのを防ぐことができる。
一度に1つの結果を算出する複雑な関数よりも、シーケンス上で動作可能な、シンプルな関数をたくさん作る方が良い手法と言える。
-
send()
,throw()
,close()
外部のクライアントコードからジェネレータ内にデータを送ることができる
動作を変更できる
ジェネレータ式: P.85
iter = (x**2 for x in range(10) if x % 2 == 0) for el in iter: print(el)
リスト内包表記に似てる
丸括弧をブラケットの代わりに使用するんだよ
yield
を使用したシンプルなループや、イテレータのように動作するリスト内包表記は、積極的にジェネレーター式に置き換えるべき
デコレータ: P.85
関数やメソッドのラッピング(受け取った関数を拡張して返す)処理の見た目をわかりやすくする
デコレータとして使用できるのは、一般的に、1つの引数(デコレーション対象)を受け取れる、名前付きの callable (呼び出し可能)オブジェクト
返り値として、他の callable オブジェクト(デコレーションした結果)を返す。
メソッドと関数に限定されない
__call__()
メソッドが定義され、 callable とみなせる任意のオブジェクトをデコレータとして使用できる。-
実装例はP.87参照のこと
# 関数として実装する例 def mydecorator(function): def wrapped(*args, **kwargs): # 実際の関数を呼び出す前に行う処理 result = function(*args, **kwargs) # 呼び出し後に行う処理 return result # ラッパーをでデコレート済み関数として返す return wrapped
# クラスとして実装する例 class DecoratorAsClass: def __init__(self, function): self.function = function def __call__(self, *args, **kwargs): # 実際の関数を呼び出す前に行う処理 result = function(*args, **kwargs) # 呼び出し後に行う処理 return result
パラメータも受け取れる => 2回ラップが行われる
メタ情報を保持するデコレータ =>
functools.wraps()
デコレータを使う
デコレータの活用例: P.90
引数チェック
キャッシュ
-
プロキシ
プロキシデコレータは関数にタグをつけたり、グローバルな仕組みへ登録したりする
たとえば、実行中のユーザーごとにコードへのアクセスを保護するセキュリティレイヤは、呼び出し可能オブジェクトに関連づけられたアクセス許可情報を利用する、集中制御型チェッカーとして実装することができます
-
コンテキストプロバイダ
@synchronized
とか
コンテキストマネージャー --- with 構文: P.98
-
try..finally
エラー発生時のクリーンアップ処理
ファイルを閉じる
ロックを解除する
一時的にコードにパッチを当てる
特定環境で保護されたコードを実行する
with
文はコードブロックの前後で何らかの処理を呼び出すためのシンプルな方法を提供しているtry..finally
文の代わりに使用できる
クラスとしてコンテキストマネージャーを実装: P.100
コンテキストマネージャープロトコルを実装したオブジェクトはコンテキストマネージャーとして使用できる
-
このプロトコルを含むこと
__enter__(self)
__exit__(self, exc_type, exc_value, traceback)
-
with
構文の実行順序__enter__()
メソッドが実行されます。メソッドの返り値はas
節で指定されたターゲットに束縛されます。with
文内のコードブロックが実行されます。-
__exit__()
メソッドが実行されます。finally
節のように後処理を行うために使われるべきですエラーが発生したときには、その例外を上げ直すべきではありません
それは呼び出し側の責任です
関数としてコンテキストマネージャーを実装: P.102
contextlib
モジュールコンテキストマネージャーと一緒に使うためにある
contextmanager
デコレータが便利らしい詳しくは P.102 を見てください(あんまりわかってない)
第1章 現在の Python のステータス
選択したプログラミング言語を深く理解することは、エキスパートとしてその言語を利用する上でもっとも大切です。これはどの技術においても常に真です。そして、言語コミュニティ内で一般的に使われているツールやプラクティスを知らずに本当に良いソフトウェアを開発するのは困難です。
venv
: 新しいバージョンのPythonを使用しているのであれば、virtualenvの代わりにvenvを使う方がよいAwesome-python
: 人気のパッケージ情報やフレームワーク情報をまとめたリストを提供していますPython Weekly
: とても人気のあるメールマガジンです。興味深いPythonパッケージやリソースについて毎週10本以上の記事を読者に配信しています。プロフェッショナルな開発者の多くは何かしらの種類のデバッガを使うのを好みます。だって。