Open6

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

tenkeitenkei

第一章

Python の歴史や取り巻く環境の説明。

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

tenkeitenkei

第二章

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の利用方法など。

tenkeitenkei

第三章

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 モジュールがピッタリ。
tenkeitenkei

第四章

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 は単なるデータ構造なので、何度でもインスタンスを作れる。

tenkeitenkei

第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)。これによって、疎結合が実現できる。インターフェースは共有するが、実装同士は独立することができる。