クラスは「データと、それに対する処理をひとまとめにする」しくみです。本記事では、ライブラリのコードを読むのに必要な範囲だけに絞って、Python のクラスを整理します。
目次
- クラスを使う動機
- クラスの基本形
- インスタンスを作る
- メソッドは「self を持つ関数」
- クラス変数とインスタンス変数
- dataclass — 定型コードを減らす
- 継承 — 既存クラスを拡張する
- ライブラリのコードを読むために
クラスを使う動機
たとえば「銘柄コード」と「社名」と「終値リスト」をひとまとまりで扱いたい場面を考えます。辞書でも書けますが、関連する処理(平均終値の計算など)も同じ場所に置くと、コードが整理しやすくなります。
# 辞書でやる場合stock = {"ticker": "7203", "name": "トヨタ自動車", "prices": [2900, 2925, 2880]}avg = sum(stock["prices"]) / len(stock["prices"])データと処理がバラバラに散らばりやすいのが弱点です。クラスにすると 1 つの単位で扱えます。
クラスの基本形
class キーワードでクラスを定義します。
class Stock: def __init__(self, ticker: str, name: str, prices: list[float]) -> None: self.ticker = ticker self.name = name self.prices = prices
def average(self) -> float: return sum(self.prices) / len(self.prices)
toyota = Stock("7203", "トヨタ自動車", [2900, 2925, 2880])print(toyota.ticker) # 7203print(toyota.average()) # 2901.666...ポイントは次のとおりです。
__init__は コンストラクタ。インスタンスを作るときに呼ばれるselfは 作成中のインスタンス自身 を指す慣習的な引数名self.xxx = ...で インスタンス変数 を定義する- メソッドは関数とほぼ同じだが、第 1 引数に
selfを取る
インスタンスを作る
Stock(...) のようにクラス名を関数のように呼び出すと、新しいインスタンスが作られます。
toyota = Stock("7203", "トヨタ自動車", [2900, 2925, 2880])softbank = Stock("9984", "ソフトバンクグループ", [9800, 9750, 9900])
for stock in [toyota, softbank]: print(f"{stock.name}: 平均 {stock.average():.0f} 円")同じクラスから何個でもインスタンスを作れます。それぞれが独立したデータを持ちます。
メソッドは「self を持つ関数」
メソッドはクラスの中で定義された関数で、第 1 引数に self を取ります。
class Stock: def __init__(self, ticker: str, prices: list[float]) -> None: self.ticker = ticker self.prices = prices
def latest(self) -> float: return self.prices[-1]
def gain(self) -> float: return (self.prices[-1] - self.prices[0]) / self.prices[0]
s = Stock("7203", [2900, 2925, 2880, 2910, 2945])print(s.latest()) # 2945print(f"{s.gain():.2%}") # 1.55%メソッドの中からは self.xxx で同じインスタンスのデータや別メソッドにアクセスできます。
クラス変数とインスタンス変数
クラス変数は すべてのインスタンスで共有 され、インスタンス変数は インスタンスごとに別々 です。
class Stock: market = "TSE" # クラス変数(共通)
def __init__(self, ticker: str) -> None: self.ticker = ticker # インスタンス変数(個別)
a = Stock("7203")b = Stock("9984")print(a.market, b.market) # TSE TSE (共通)print(a.ticker, b.ticker) # 7203 9984 (個別)可変な値(リストなど)をクラス変数に置くと意図しない共有が起きます。原則として 値を持つのはインスタンス変数 だけにするのが安全です。
dataclass — 定型コードを減らす
「データを保持するクラス」を簡潔に書きたいとき、標準ライブラリの dataclasses モジュールが便利です。
from dataclasses import dataclass
@dataclassclass Stock: ticker: str name: str prices: list[float]
def average(self) -> float: return sum(self.prices) / len(self.prices)
s = Stock("7203", "トヨタ自動車", [2900, 2925, 2880])print(s) # Stock(ticker='7203', name='トヨタ自動車', prices=[2900, 2925, 2880])print(s.average()) # 2901.666...@dataclass を付けると、__init__ や __repr__(表示用の文字列)が自動で作られます。データ中心のクラスでは積極的に使えます。
継承 — 既存クラスを拡張する
既存のクラスを土台にして新しいクラスを作るのが継承です。
class Asset: def __init__(self, ticker: str, prices: list[float]) -> None: self.ticker = ticker self.prices = prices
def latest(self) -> float: return self.prices[-1]
class Stock(Asset): def __init__(self, ticker: str, prices: list[float], market: str) -> None: super().__init__(ticker, prices) self.market = market
s = Stock("7203", [2900, 2925], market="TSE")print(s.latest(), s.market) # 2925 TSEsuper().__init__(...) で親クラスの初期化を呼び出します。継承を使うかは状況次第ですが、ライブラリのコードを読むときに意味が分かる程度には把握しておくと役立ちます。
ライブラリのコードを読むために
pandas や requests などのライブラリは、ほとんどがクラスで書かれています。クラスを読むときは次の順で見ると把握しやすくなります。
- クラス名と
__init__のシグネチャ → どんなデータを保持するか - 公開メソッドの一覧 → 何ができるか
- アンダースコアで始まる名前(
_xxx) → 内部用の補助メソッド
「自分でゼロから設計する」前に、「他人のクラスを読み解く」ところから始めると無理がありません。
生成AI へのプロンプト例
クラス設計を相談する例です。
Python 3.12 で、株価の時系列を保持する小さなクラスを書いてください。
要件:- dataclass で定義する- フィールド: ticker (str), prices (list[float])- メソッド: latest() / average() / max_drawdown()- max_drawdown は「最高値からの最大下落率」を負の値で返す- 各メソッドに 1 行の docstring を付ける- 標準ライブラリのみ要件・型・サンプルを示すと、設計の意図が伝わりやすくなります(#7-2「データ分析のためのプロンプト設計」)。
まとめ
- クラスは「データと処理をまとめる単位」
__init__でインスタンスを初期化し、メソッドの第 1 引数はself- データ中心のクラスは
@dataclassで簡潔に書ける - ライブラリのコードを読むには、クラス名・
__init__・公開メソッドを順に追う