この単位では、Pythonにおけるクラス、オブジェクト、オブジェクト指向の基本を扱う。
class__init__- インスタンス属性
- インスタンスメソッド
- クラス属性
- クラスメソッド
- スタティックメソッド
propertydataclasssuper()- 継承
- メソッドオーバーライド
- 合成
- duck typing の基本
- 特殊メソッドの基礎
__repr____str____eq__
- 抽象基底クラスの最小限
Enumの最小限
この単位の主な論点は次の通り。
classを使うと、状態と振る舞いをまとめた型を定義できる__init__は、インスタンス生成時の初期化処理として使う- インスタンス属性は、インスタンスごとに異なる状態を持てる
- インスタンスメソッドは、
selfを通してインスタンスの状態を扱う - クラス属性は、クラスに属する共有の値として使える
classmethodは、クラス自身を受け取るメソッドとして使うstaticmethodは、selfやclsを使わない関連処理として使うpropertyは、計算結果を属性のように見せたい場合に使えるdataclassは、値を持つクラスの定型コードを減らせるsuper()は、親クラスの処理を呼び出すために使う- 継承とメソッドオーバーライドにより、共通処理と差分処理を分けられる
- 合成は、別のオブジェクトを部品として持つ設計である
- duck typing では、継承関係よりも必要な振る舞いを持つかを重視する
- 特殊メソッドにより、文字列表現や等価性を定義できる
- 抽象基底クラスは、実装すべきメソッドの形を定義できる
Enumは、取りうる値を限定した名前付きの定数群として使える
この単位のファイル構成は次の通り。
src/11_classes_objects_and_oop/
main.py
class_basics.py
method_types_and_property.py
dataclass_examples.py
inheritance_and_super.py
composition_and_duck_typing.py
abc_and_enum.py
各ファイルの役割は次の通り。
main.py- Unit 11 の実行入口
- 各テーマ別ファイルの関数を順番に呼び出す
class_basics.pyclass、__init__、インスタンス属性、インスタンスメソッド、クラス属性を扱う
method_types_and_property.pyclassmethod、staticmethod、propertyを扱う
dataclass_examples.pydataclass、__repr__、__str__、__eq__を扱う
inheritance_and_super.py- 継承、
super()、メソッドオーバーライドを扱う
- 継承、
composition_and_duck_typing.py- 合成、duck typing の基本を扱う
abc_and_enum.py- 抽象基底クラス、
Enumを扱う
- 抽象基底クラス、
リポジトリ直下で仮想環境を有効化してから実行する。
PowerShell の場合:
.venv\Scripts\Activate.ps1
python src/11_classes_objects_and_oop/main.pyGit Bash の場合:
source .venv/Scripts/activate
python src/11_classes_objects_and_oop/main.pyRuff の確認は次のコマンドで行う。
uv run ruff check .
uv run ruff format --check .必要に応じてフォーマットを実行する。
uv run ruff format .次の順番で読むと、内容を追いやすい。
main.pyclass_basics.pymethod_types_and_property.pydataclass_examples.pyinheritance_and_super.pycomposition_and_duck_typing.pyabc_and_enum.py
最初に main.py を読むことで、この単位全体の実行順序を把握できる。
その後、クラスの基本、メソッド種別、dataclass、継承、合成、抽象基底クラスと Enum の順番で読む。
Unit 11 全体の処理の流れは次の通り。
main.pyが実行されるmain()が呼び出される- 表示用の見出しを出す
- クラス定義、インスタンス属性、インスタンスメソッドのサンプルを実行する
classmethod、staticmethod、propertyのサンプルを実行するdataclassと特殊メソッドのサンプルを実行する- 継承、
super()、メソッドオーバーライドのサンプルを実行する - 合成と duck typing のサンプルを実行する
- 抽象基底クラスと
Enumのサンプルを実行する - 各ファイル内の
assertにより、軽い期待値確認を行う
この単位では、Python におけるオブジェクト指向の基本的な書き方を主題にしている。
Java のクラス構文と似ている部分もあるが、self、dataclass、duck typing など Python らしい違いも確認する。
class_basics.py では、User クラスのインスタンス属性を扱う。
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
self.status = User.default_statusself.name や self.age は、インスタンスごとに保持される値である。
Java の this に近いが、Python ではメソッドの第1引数として明示的に self を書く。
インスタンスメソッドでは、self を通してインスタンス属性を参照したり更新したりする。
method_types_and_property.py では、文字列から Product を作成している。
@classmethod
def from_text(cls, text: str) -> "Product":
name, price_text = text.split(",")
return cls(name.strip(), int(price_text.strip()))classmethod の第1引数 cls は、クラス自身を表す。
この例では、通常の __init__ とは別に、カンマ区切り文字列からインスタンスを作る入口を用意している。
ファクトリメソッドのような使い方をしたい場合に向いている。
method_types_and_property.py では、税込価格を property として定義している。
@property
def tax_included_price(self) -> int:
return int(self.base_price * (1 + Product.tax_rate))property を使うと、呼び出し側は product.tax_included_price のように属性として参照できる。
内部では計算しているが、利用側からは値を読むだけの属性に見える。
メソッド呼び出しにするか、property にするかは、利用側から見た自然さを考えて選ぶ。
dataclass_examples.py では、座標を dataclass で定義している。
@dataclass
class Point:
x: int
y: intdataclass を使うと、__init__、__repr__、__eq__ などが自動生成される。
値を保持することが主目的のクラスでは、通常の class より簡潔に書ける。
同じ属性値を持つ Point 同士が等しいと判定される点にも注目する。
inheritance_and_super.py では、子クラスから親クラスの初期化処理を呼び出している。
class EmailNotification(Notification):
def __init__(self, recipient: str, subject: str) -> None:
super().__init__(recipient)
self.subject = subjectsuper().__init__(recipient) により、親クラスの __init__ を呼び出している。
共通の初期化処理は親クラスに置き、子クラスでは差分だけを追加できる。
継承では、共通処理と個別処理の分け方が重要となる。
composition_and_duck_typing.py では、ReportService が送信役を属性として持っている。
class ReportService:
def __init__(self, sender: Sender) -> None:
self.sender = senderこのように、別のオブジェクトを属性として持つ設計を合成と呼ぶ。
ReportService は送信処理の詳細を知らず、sender.send(...) に処理を委譲している。
送信役を ConsoleSender や MemorySender に差し替えられるため、継承より柔軟に扱える場面がある。
abc_and_enum.py では、Formatter を抽象基底クラスとして定義している。
class Formatter(ABC):
@abstractmethod
def format(self, text: str) -> str:
raise NotImplementedError抽象基底クラスは、子クラスが実装すべきメソッドの形を定義できる。
UpperFormatter や PrefixFormatter は、format メソッドを実装することで具体的な処理を持つ。
最低限の使い方としては、共通のインターフェースを示したい場合に使える。
class_basics.py では、default_status をクラス属性として定義している。
class User:
default_status = "active"
def __init__(self, name: str, age: int) -> None:
self.status = User.default_statusdefault_status はクラスに属する値である。
一方、self.status はインスタンスごとの値である。
共通の既定値として使うものと、インスタンスごとに変わる状態は分けて考える。
method_types_and_property.py では、価格判定を staticmethod として定義している。
@staticmethod
def is_valid_price(price: int) -> bool:
return price >= 0staticmethod は、self や cls を使わない処理をクラス内に置くための仕組みである。
ただし、クラスと関係が薄い処理まで入れると、責務が分かりにくくなる。
クラスに関連する補助処理かどうかを考えて使う。
dataclass_examples.py では、Task に __repr__ と __str__ を定義している。
def __repr__(self) -> str:
return f"Task(title={self.title!r}, priority={self.priority!r})"
def __str__(self) -> str:
return f"{self.title} / priority={self.priority}"__repr__ は、開発者向けの表現として使われることが多い。
__str__ は、ユーザー向けに読みやすい表現として使われることが多い。
どちらも文字列表現だが、想定する読み手が違う。
inheritance_and_super.py では、Notification を親クラスにしている。
class EmailNotification(Notification):
def build_message(self, body: str) -> str:
base_message = super().build_message(body)
return f"[{self.subject}] {base_message}"継承を使うと、親クラスの共通処理を再利用できる。
一方で、親子関係が深くなると、どの処理がどこで定義されているか追いにくくなる。
共通処理の再利用だけが目的なら、合成の方が分かりやすい場合もある。
composition_and_duck_typing.py では、deliver_message が send を使っている。
def deliver_message(sender: Sender, message: str) -> str:
return sender.send(message)重要なのは、渡されたオブジェクトが send メソッドを持っていることである。
同じ親クラスを継承しているかどうかだけが基準ではない。
Python では、このように必要な振る舞いに注目する考え方がよく出てくる。
abc_and_enum.py では、タスク状態を Enum として定義している。
class TaskStatus(Enum):
TODO = "todo"
DOING = "doing"
DONE = "done"文字列を直接あちこちに書くと、タイポや表記ゆれが起きやすい。
Enum を使うと、取りうる値を名前付きでまとめられる。
状態や種別のように候補が決まっている値では、Enum が使いやすい場合がある。
この単位を読んだ後、次の内容を確認する。
classと__init__の基本的な役割を説明できる- インスタンス属性とクラス属性の違いを説明できる
- インスタンスメソッドで
selfを使う理由を説明できる classmethod、staticmethod、propertyの違いを説明できるdataclassが定型コードを減らすことを説明できる__repr__、__str__、__eq__の基礎を説明できる- 継承、
super()、メソッドオーバーライドの基本を説明できる - 合成と継承の違いを説明できる
- duck typing の基本的な考え方を説明できる
- 抽象基底クラスの最小限の使い方を説明できる
Enumの基本的な用途を説明できる