「エキスパートPythonプログラミング 改訂4版」を読んだ

第一章
Python の歴史や取り巻く環境の説明。
Slackコミュニティとニュースレターの紹介が良かった。Slackコミュニティに参加し、PythonWeeklyのニュースレターを購読してみた。
また、Python にも Awesome レポジトリ(https://github.com/vinta/awesome-python )があることを知った。

第二章
Python の開発環境について。
多くのOSでは標準コンポーネントとして Python がインストールされている。が、バージョンが少し古い。
また、パッケージについては PyPI のものを使うべきで、pip コマンドを利用してインストールする。
$ python3 -m ensurepip
pip コマンドでインストールしたものは site-packages
ディレクトリに保存される。
# 確認
$ python3 -m site
しかし、 pip でインストールを行うと、グローバルな site-packages に保存されてしまう。これは避けるべき。
そこで利用できるのが環境分離。そのための機能として、 Python には venv というものがある。
また、依存パッケージの管理には Poetry が利用できる。Poetry の設定ファイルは pyproject.toml を作成する。
それ以外には、Docker や Vagrant を使った仮想環境の作成方法や、IPythonの利用方法など。

第三章
Python3での新機能について。
辞書のマージと更新の演算子
演算子によってリストやタプル同士を結合したり、要素を追加できる。
[1,2,3] + [4,5,6]
value = (1,2,3)
value += (4,5,6)
セット同士の積、和、差、対称差を求めることもできる。
{1,2,3} & {4,5} # {1}
{1,2,3} | {1,4} # {1,2,3,4}
{1,2,3} - {1,4} # {2,3}
{1,2,3} ^ {1,4} #{2,3,4}
辞書同士の結合とインプレース更新も可能。
{‘a’:1} | {‘a‘:3, ‘b’: 2} # {‘a’: 3, ‘b’: 2}
my_dict = {‘a’: 1}
my_dict |= {‘a’: 3, ‘b’: 2} # {‘a’: 3, ‘b’: 2}
以下のようなアンパックによる結合をする必要はもうない。
a = {‘a’: 1}; b = {‘a’:3, ‘b’: 2}
{**a, **b}
代入式(セイウチ演算子)とは式だけど変数に値を割り当てることができる。
isOk = True
if isOk:
return
を
```py
if isOk = True:
return
のようにスッキリと書ける。以下のような場合にも有効。
first_name = “foo”; last_name = “bar”
user = {
“first_name”: first_name,
“last_name”: last_name,
“display_name”: f”{first_name} {last_name}”
}
代入式を使うと以下の通り。
user = {
“first_name”: (first_name := “foo”),
“last_name”: (last_name := “bar”),
“display_name”: f”{first_name} {last_name}”
}
構造的パターンマッチ
構造的パターンマッチとは match-case 構文。
ちなみに match はソフトキーワード、つまり予約語ではないので普通に変数名としても使える。
以下は、FizzBuzz問題を構造的パターンマッチで実装した例。
for i in range(100):
match (i % 3, i % 5):
case (0, 0): print(“FizzBuzz”)
case (0, _): print(“Fizz”)
case (_, 0): print(“Buzz”)
case _: print(i)
最後のアンダースコアはワイルドカードパターン。
型ヒントのジェネリクス型
typing の Dict, List, Tuple, Set, FrozenSet は非推奨となっており、将来削除される。Python3.9以降では、組み込みのジェネリック型を使って型ヒントを指定するべき。
Union型と|演算子
Union型を表現する場合に、def foo(key: string | bytes): のように書ける。ジェネリクス型と異なり、typingのUnionが非推奨になっているわけではない。
位置専用引数
Pythonで関数に引数を渡すには、2つの方法がある。位置引数とキーワード引数。
以下のように書くと、引数の渡し方をより限定できる。
def concatenate(first: str, second: str, /, *, delim: str):
return delim.join([first, second])
/ より前は位置引数、* より後はキーワード引数。
zoneinfo モジュール
以下、ZoneInfoモジュールを利用して、タイムゾーン対応の datetime オブジェクトを作成する例。
from datetime import datetime
from zoneinfo import ZoneInfo
dt = datetime(2020, 11, 28, tzinfo=ZoneInfo(“Asia/Tokyo”))
その他
以前からPythonにあるけど、特筆すべき機能。
- f-stringは便利
- 数値リテラルにはアンダースコアが使える(例: 1_000_000)
- randomモジュールの生成する乱数は条件によっては予測可能なので、パスワードやトークンなどのセキュリティ目的に使うべきではない。セキュリティ目的なら、secrets モジュールがピッタリ。

第四章
Pythonとほかの言語の比較。
オブジェクト指向プログラミング
PythonはKotkinと似ている。
Pythonでは多重継承が可能だが、良いプラクティスかは疑問。また、private/publicといったアクセス制御キーワードを提供しない。
クラスインスタンスの初期化
Pythonではクラスプロパティとして定義せずとも、 __init__
メソッドから初期化できる。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
演算子のオーバーロード
Pythonの数多くの演算子は、dunderメソッドとしてエクスポーズされている。
例えば、+
演算子は __add__
としてアクセス可能。そしてこれらは上書きすることができる。
class DoubleAdder:
def __add__(a: int, b: int):
return (a+b)*2
double_adder = DoubleAdder()
double_adder.add(1,2) # 6
map, filter, reduce
map, filter は import せずに利用可能だが、reduce は functools から import する。
ジェネレーター
試行回数が無限のような処理では、ジェネレータが有効。
def fibonacci():
a, b = 0, 1
while True:
yield b
a, b = b, a + b
>>> fib = fibonacci()
>>> next(fib)
1
>>> next(fib)
2
デコレータ
関数に加えて、あらゆる callable なものに付与可能。
デコレータは単なるシンタックスシュガー。
@decorate_this
def decorated_function():
pass
def decoreted_functions():
decoreted_functions = decorate_this(decoreted_functions)
列挙型
Enum や Flag など。値が固定で、グローバルに1度だけ定義されるような場合に有効。
もし値自体に意味がない場合は、auto を利用するとよい。
from enum import Enum, auto
class OrderStatus(Enum):
PENDING = auto()
PROCESSING = auto()
PROCESSED = auto()
辞書型や named tuple は単なるデータ構造なので、何度でもインスタンスを作れる。

第5章 インターフェイス、パターン、モジュール化
zope.interface
昔に人気だったが今では使われなくなってきているライブラリ。インターフェイスの定義や検証が可能となる。
関数アノテーションや抽象基底クラス
抽象基底クラス(ABC)について。
from abc import ABC, abstractmethod
class DummyInterface(ABC):
@abstractmethod
def dummy_method(self): ...
@property
@abstractmethod
def dummy_property(self): ...
型アノテーション
typing を利用して型アノテーションを作成可能。mypyなどを利用すれば、静的型解析が可能。
制御の反転と依存性注入
伝統的なアーキテクチャでは、上位のレイヤーが下位のレイヤーを呼び出す。
制御の反転(Inverse of Control)はその逆。ポリモーフィズム、関数を引数で渡す、デコレータ、クロージャなど。デザインパターンではなく、設計の特性。
制御の反転の中でも最も重要なものが、依存性注入(Dependency Injection)。これによって、疎結合が実現できる。インターフェースは共有するが、実装同士は独立することができる。
