>100 Views
October 14, 26
スライド概要
はじめまして、yukikoと申します。 IT教育支援や、DX推進が可能です。 ◆ スキル LPIC レベル2 AI / Python Splunk BI(データ可視化・分析) ◆ その他 新卒・未経験の学生向けに、エンジニア転職を応援する資料を趣味で作成しています。 もしよろしければご活用ください。
UMLクラス図編に続く、実装の第 2部 オブジェクト指向 Python実装編 「設計の考え方」を、動くコードに落とし込む 査読済み実証研究 × Python公式ドキュメント/PEP に基づく構成 うさうさ研修工房 | 監修:うさうさ先生
ROADMAP この資料の歩き方 CHAPTER 1 CHAPTER 2 CHAPTER 3 クラスとインスタンス カプセル化 継承とポリモーフィズム 設計図と実体の違い/__init__/selfの正体 「見せる/隠す」を使い分ける 共通化と、呼び出し側から見た統一 CHAPTER 4 CHAPTER 5 CHAPTER 6 抽象基底クラスと構成 Pythonらしい書き方 総合演習と設計の指針 ABC/継承 vs コンポジション dataclass/特殊メソッド 図書館システム/査読研究からの知見 前提:UMLクラス図編で学んだ「箱と線」が、ここからは実際に動くPythonコードになります。 うさうさ研修工房|オブジェクト指向 Python実装編 2
CHAPTER 1 クラスとインスタンス 設計図と、そこから作られる「実体」を区別する
CLASS vs INSTANCE クラスは「設計図」、インスタンスは「実物」 book1 : Book Book + title : str + price : int title = "吾輩は猫である" price = 800 覚え方 クラス=たい焼きの「型」 インスタンス=型から焼いた「たい焼き」 Book(...)で生成 型は1つでも、中身(あんこ・クリーム)が違 うたい焼きをいくつも作れる。 book2 : Book + lend() 設計図(クラス)は 1つ title = "坊っちゃん" price = 650 book3 : Book title = "こころ" price = 720 book1 = Book( "吾輩は猫である", 800) book2 = Book( "坊っちゃん", 650) book1.lend() 実物(インスタンス)は何個でも うさうさ研修工房|オブジェクト指向 Python実装編 4
CONSTRUCTOR __init__ — 生まれた瞬間に呼ばれるメソッド book.py 実行時に起きていること class Book: def __init__(self, title, price): self.title = title self.price = price 1 Book("こころ", 720) が呼ばれる book = Book("こころ", 720) 2 空のインスタンスが用意される 3 __init__ が自動で呼ばれる 4 self に値が詰められて返る つまずきポイント __init__ は「コンストラクタ」と呼ばれますが、正確にはインスタンスを作る関数ではなく、作ら れた直後の初期化を担当するメソッドです。戻り値は書きません( return しない)。 うさうさ研修工房|オブジェクト指向 Python実装編 5
WHAT IS SELF self の正体 —「自分自身」が黙って渡されている あなたが書くコード self.py book.lend() Pythonが内部で読み替える class Book: def __init__(self, title): self.title = title def lend(self): # self には book が入っている print(f"{self.title} を貸出") Book.lend(book) 実際の呼び出し(第1引数=そのインスタンス) ここだけ押さえる メソッドの第1引数 self は「このメソッドを呼び出したインスタンス自身」。だからself.title と書けば、book1 なら「吾輩は猫である」、book2 なら「坊っちゃん」と、同じコードがインスタン スごとに違う値を扱えます。 うさうさ研修工房|オブジェクト指向 Python実装編 6
CHAPTER 2 カプセル化 「見せる」と「隠す」を意識して設計する
ENCAPSULATION
カプセル化 — 触ってほしくない所は隠す
account.py
BankAccount
class BankAccount:
def __init__(self, balance: int):
self.__balance = balance
# 非公開
__balance(隠す)
直接書き換えられたら残高が壊れる
deposit() / withdraw()(見せる)
def deposit(self, amount: int):
if amount <= 0:
raise ValueError("金額が不正です")
self.__balance += amount
def get_balance(self) -> int:
return self.__balance
必ずチェックを通してから変更する
acc = BankAccount(1000)
acc.deposit(500)
# OK
acc.__balance = -999 # 外からは変えられない
補足:Pythonの __ は名前修飾(name mangling)による「事故防止」であり、他言語のような厳密なアクセス制限ではありません。
うさうさ研修工房|オブジェクト指向 Python実装編
8
PROPERTY
@property — 「隠す」と「使いやすさ」を両立する
property.py
class Book:
def __init__(self, price: int):
self.__price = price
何がうれしいのか
book.get_price() ではなく book.price と書けるのに、値の代入時にはチェッ
クが走ります。使う側の書き味はそのままに、守りだけを足せます。
@property
def price(self) -> int:
return self.__price
@price.setter
def price(self, value: int):
if value < 0:
raise ValueError("価格は0以上")
self.__price = value
book = Book(720)
print(book.price)
book.price = 900
book.price = -100
# 720
# setterが動く
# ValueError
公式ドキュメント: https://docs.python.org/ja/3/library/functions.html#property
うさうさ研修工房|オブジェクト指向 Python実装編
9
CHAPTER 3 継承とポリモーフィズム 共通部分をまとめ、呼び出し側を単純にする
INHERITANCE 継承 — 共通部分は親に、違いだけ子に書く Item + title + price + describe() Book + author DVD + minutes inheritance.py class Item: def __init__(self, title, price): self.title, self.price = title, price class Book(Item): def __init__(self, title, price, author): super().__init__(title, price) # 親の初期化を呼ぶ self.author = author うさうさ研修工房|オブジェクト指向 Python実装編 11
POLYMORPHISM ポリモーフィズム — 呼び出し側は中身を知らなくていい Book こころ(720円)/著者 夏目漱石 DVD 七人の侍( 1500円)/207分 Magazine 月刊うさうさ( 600円)/2026年9月号 for item in items: item.describe() 呼び出し側は1種類だけ polymorphism.py class Book(Item): def describe(self): return f"{self.title}({self.price}円)/著者 {self.author}" class DVD(Item): def describe(self): return f"{self.title}({self.price}円)/{self.minutes}分" items = [Book(...), DVD(...), Magazine(...)] for item in items: print(item.describe()) # 中身が何かを気にせず同じ書き方で呼べる うさうさ研修工房|オブジェクト指向 Python実装編 12
CHAPTER 4 抽象基底クラスと構成 「約束」を決め、継承に頼りすぎない
ABSTRACT BASE CLASS 抽象基底クラス( ABC)— 実装を強制する「約束」 «abstract» Notifier + send(message) MailNotifier + send(message) notifier.py from abc import ABC, abstractmethod class Notifier(ABC): @abstractmethod def send(self, message: str): ... SlackNotifier + send(message) なぜ使うのか send() を実装し忘れたクラスは、インスタンス化しようとした時 点で TypeError になります。「あとで実装するつもりだった」とい う漏れを、実行前に潰せます。 class MailNotifier(Notifier): def send(self, message: str): print("メール送信:", message) うさうさ研修工房|オブジェクト指向 Python実装編 14
INHERITANCE vs COMPOSITION 継承 vs コンポジション — 迷ったら「持つ」を選ぶ 継承(is-a)「〜である」 コンポジション( has-a)「〜を持つ」 Item Order Book は Item の一種であ る Notifier Book Order は Notifier を持つ 継承 コンポジション class Book(Item): ... class Order: def __init__(self, notifier): self.notifier = notifier # 親が変わると子も影響を受ける # 階層が深いほど追いづらい # 差し替えやすく、テストしやすい 判断の目安:「 AはBの一種か?」と聞かれてしっくり来なければ、継承ではなくコンポジションを検討する。 うさうさ研修工房|オブジェクト指向 Python実装編 15
CHAPTER 5 Pythonらしい書き方 同じ設計を、より短く・より読みやすく
DATACLASS
dataclass — 定型コードを書かずに済ませる
Before
class Book:
def __init__(self, title, price):
self.title = title
self.price = price
def __repr__(self):
return f"Book({self.title!r}, {self.price})"
def __eq__(self, other):
return (self.title, self.price) == \
(other.title, other.price)
After
from dataclasses import dataclass
@dataclass
class Book:
title: str
price: int
# __init__ / __repr__ / __eq__ が
# 自動で生成される
使いどころ
「値を持つのが主な仕事」のクラス(設定・レコード・ DTOなど)に向いています。逆に、複雑な振る舞いが中心のクラスは通常のクラスのままで構いません。公式
:https://docs.python.org/ja/3/library/dataclasses.html
うさうさ研修工房|オブジェクト指向 Python実装編
17
SPECIAL METHODS 特殊メソッド — Pythonの文法にクラスを馴染ませる メソッド 役割 __str__ print(obj) の表示を決める print(book) __repr__ デバッグ時の表示を決める repr(book) __eq__ == の比較ルールを決める book1 == book2 __len__ len() の結果を決める len(shelf) __iter__ for で回せるようにする for b in shelf: class Book: def __str__(self): return f"『{self.title}』" うさうさ研修工房|オブジェクト指向 Python実装編 使う側の書き方 # print(book) → 『こころ』 18
CHAPTER 6 総合演習と設計の指針 全部つなげて、根拠を持って設計する
CASE STUDY: DIAGRAM 総合演習 — 図書館の貸出システム «abstract» Library Item 1..* + title + price + items + add(item) + lend(title) + describe() Book + author DVD 0..* + minutes Member + name 読み方:Library は1つ以上の Item を保有し(合成)、 Item には Book と DVD という具体型がある(継承)。 Library は 0人以上の Member と関連を持つ。 うさうさ研修工房|オブジェクト指向 Python実装編 20
CASE STUDY: CODE
図から書き起こした Pythonコード
items.py
library.py
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class Member:
name: str
class Item(ABC):
def __init__(self, title: str, price: int):
self.title = title
self.price = price
@abstractmethod
def describe(self) -> str: ...
class Book(Item):
def __init__(self, title, price, author):
super().__init__(title, price)
self.author = author
def describe(self):
return f"『{self.title}』{self.author}"
うさうさ研修工房|オブジェクト指向 Python実装編
class Library:
def __init__(self):
self.items: list[Item] = []
self.members: list[Member] = []
def add(self, item: Item):
self.items.append(item)
def lend(self, title: str) -> str:
for item in self.items:
if item.title == title:
return item.describe()
raise ValueError("在庫がありません")
21
EVIDENCE-BASED DESIGN 査読済み研究が示す「設計の効きどころ」 ① 設計の複雑さは「測れる」ものとして提案されてきた クラスの規模、継承の深さ、他クラスとの結合度などを指標化する CKメトリクス群が提案され、以後のオブジェクト指向設計研究の共通基盤になった。 出典:Chidamber & Kemerer (1994), IEEE Transactions on Software Engineering, 20(6), 476–493 ② 設計段階の指標は、後の不具合の予測に役立ちうる CKメトリクスの一部は、クラスが不具合を含みやすいかどうかを開発初期に予測する指標として有用であり、従来のコード量ベースの指標より優れていたと報告されている。 出典:Basili, Briand & Melo (1996), IEEE Transactions on Software Engineering, 22(10), 751–761 ③ 継承を深くしすぎない・結合を強くしすぎない 設計の複雑さを表す指標と実際の不具合との関連が実証的に分析されており、複雑さを抑える設計判断に経験則以上の根拠があることを示している。 出典:Subramanyam & Krishnan (2003), IEEE Transactions on Software Engineering, 29(4), 297–310 → 「継承は浅く、結合はゆるく」という本資料の方針は、これらの実証研究の流れに沿ったものです。 うさうさ研修工房|オブジェクト指向 Python実装編 22
OFFICIAL SOURCES & REFERENCES 一次情報にあたるための参考リンク 公式ドキュメント Python公式チュートリアル:クラス https://docs.python.org/ja/3/tutorial/classes.html abc — 抽象基底クラス https://docs.python.org/ja/3/library/abc.html dataclasses — データクラス https://docs.python.org/ja/3/library/dataclasses.html データモデル(特殊メソッド一覧) https://docs.python.org/ja/3/reference/datamodel.html PEP 8 — Pythonコーディング規約 https://peps.python.org/pep-0008/ 査読済み参考論文 Chidamber, S. R. & Kemerer, C. F. (1994). A Metrics Suite for Object Oriented Design. IEEE Transactions on Software Engineering, 20(6), 476–493. Basili, V. R., Briand, L. C. & Melo, W. L. (1996). A Validation of Object-Oriented Design Metrics as Quality Indicators. IEEE TSE, 22(10), 751–761. Subramanyam, R. & Krishnan, M. S. (2003). Empirical Analysis of CK Metrics for Object-Oriented Design Complexity. IEEE TSE, 29(4), 297–310. うさうさ研修工房|オブジェクト指向 Python実装編 23
CHEAT SHEET 困ったときの 1枚チートシート クラスの基本形 線の記法 → 実装 class Book(Item): def __init__(self, title): super().__init__() self.title = title ── 関連 相手をリストや属性で保持する ◇── 集約 外で作った相手を受け取る ◆── 合成 自分の中で相手を生成する ──▷ 継承 class 子(親): + super() --▷ 実現 ABC を継承し全メソッド実装 「値を入れるだけのクラス?」 --> 依存 引数で受け取って使うだけ はい → @dataclass で十分 def lend(self): return self.title 迷ったときの判断 「AはBの一種?」 はい → 継承 / いいえ → 持つ(コンポジション) 「外から触られたら困る?」 はい → __ で隠し、メソッド経由で変更させる 可視性(UML → Python) + public / そのまま self.x - private / self.__x # protected / self._x ~ package / 直接の対応なし 多重度の書き方 1 0..* 1..* 単一の属性 self.x = x 「実装を忘れたら困る?」 よく使う道具 super() 親の処理を呼ぶ @property 属性のように使えるメソッド ABC 実装を強制する抽象クラス @dataclass __init__等を自動生成 __str__ print時の表示を決める リスト self.xs = [] リスト+空チェック うさうさ研修工房|オブジェクト指向 Python実装編 はい → ABC + @abstractmethod つまずいたら見るところ TypeError: abstract… → @abstractmethod の実装漏れ AttributeError: __x → 名前修飾。外からは触れない 24
まとめ • クラスは設計図、インスタンスは実体。 self は「呼び出したインスタンス自身」 YourClass • カプセル化は「壊されたくない状態」を守るための仕組み • 継承は共通化、ポリモーフィズムは呼び出し側を単純にするための道具 + ??? • 迷ったら継承より「持つ(コンポジション)」を先に検討する • dataclass や特殊メソッドで、同じ設計を Pythonらしく短く書ける 次の一歩 第1部で描いたクラス図を、実際にPythonで書き起こしてみましょう。図に描けても実装で詰まる箇所こそ、 設計が曖昧なまま残っていたサインです。 うさうさ研修工房 | 監修:うさうさ先生 + ???()