クラスは「データと、それに対する処理をひとまとめにする」しくみです。本記事では、ライブラリのコードを読むのに必要な範囲だけに絞って、Python のクラスを整理します。

目次

  1. クラスを使う動機
  2. クラスの基本形
  3. インスタンスを作る
  4. メソッドは「self を持つ関数」
  5. クラス変数とインスタンス変数
  6. dataclass — 定型コードを減らす
  7. 継承 — 既存クラスを拡張する
  8. ライブラリのコードを読むために

クラスを使う動機

たとえば「銘柄コード」と「社名」と「終値リスト」をひとまとまりで扱いたい場面を考えます。辞書でも書けますが、関連する処理(平均終値の計算など)も同じ場所に置くと、コードが整理しやすくなります。

# 辞書でやる場合
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) # 7203
print(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()) # 2945
print(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
@dataclass
class 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 TSE

super().__init__(...) で親クラスの初期化を呼び出します。継承を使うかは状況次第ですが、ライブラリのコードを読むときに意味が分かる程度には把握しておくと役立ちます。

ライブラリのコードを読むために

pandas や requests などのライブラリは、ほとんどがクラスで書かれています。クラスを読むときは次の順で見ると把握しやすくなります。

  1. クラス名と __init__ のシグネチャ → どんなデータを保持するか
  2. 公開メソッドの一覧 → 何ができるか
  3. アンダースコアで始まる名前(_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__・公開メソッドを順に追う