Q1スタティックメソッド を定義するときに付けるデコレータはどれですか?
インスタンス・クラス・スタティックメソッド — 3 種類のメソッド
Python のメソッドの 3 種類 — インスタンス・クラス・スタティック — の違いを図解で解説します。self / cls / 引数なしの使い分けまで押さえます。
前回まででクラス変数とインスタンス変数の違いまで押さえてきました。ここまで書いてきた def method(self, ...) 形式のメソッドはすべて インスタンスメソッド ですが、Python のクラスにはあと 2 種類のメソッド — クラスメソッド と スタティックメソッド — があります。今回は 3 種類の違いと使い分けの判断軸を整理します。
メソッドは 3 種類ある
Python のクラスに定義できるメソッドは、第 1 引数に何を取るかで 3 種類に分かれます。
- インスタンスメソッド — 第 1 引数 self。インスタンス自身を受け取る
- クラスメソッド — 第 1 引数 cls。クラス自身を受け取る。@classmethod を付ける
- スタティックメソッド — 第 1 引数なし。self も cls も受け取らない。@staticmethod を付ける
どの種類かによって、何にアクセスできるか が決まります。インスタンス変数 self.x に触れるのはインスタンスメソッドだけです。クラス変数を クラス側から使うのがクラスメソッド、どちらにも触れずに純粋な処理だけ書くのがスタティックメソッドです。
インスタンスメソッド — self を取る
ここまでずっと書いてきた def method(self, ...) 形式が インスタンスメソッド です。第 1 引数 self には、メソッドを呼び出したインスタンス自身が自動で入るので、self.name のように インスタンス変数 にアクセスできます。
apple.show() と書いたとき、Python はこれを内部的に Product.show(apple) に変換しています。「メソッドはインスタンスを引数に取る関数」 と見ることができます。
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def total_with_tax(self, tax_rate): # インスタンスメソッド
return int(self.price * (1 + tax_rate))
apple = Product("りんご", 150)
print(apple.total_with_tax(0.1)) # 165
# 内部的にはこれと同じ
print(Product.total_with_tax(apple, 0.1)) # 165
__init__ が apple.price = 150 を設定 → ② apple.method(0.1) を呼ぶと self に apple が入る → ③ self.price で インスタンスに設定された値 が読める。クラスメソッド — @classmethod と cls
クラスメソッド は、@classmethod というデコレータを付けて定義し、第 1 引数を cls にします(慣習的な名前)。cls には クラス自身(この場合 Product という設計図そのもの)が自動で入ります。
クラスメソッドは クラス変数を読み書きするとき や、インスタンスを 1 つ作って返す(ファクトリ) ときに使います。apple.method() のようにインスタンス経由でも呼べますが、Product.method() とクラスから直接呼ぶのが一般的です。
class Product:
total_count = 0 # クラス変数
def __init__(self, name, price):
self.name = name
self.price = price
Product.total_count += 1
@classmethod # ① クラス変数を返す
def get_total(cls):
return f"商品は {cls.total_count} 個登録されています"
@classmethod # ② インスタンスを作って返す(ファクトリ)
def from_csv_row(cls, row):
name, price = row.split(",")
return cls(name, int(price))
Product("りんご", 150)
Product("バナナ", 80)
print(Product.get_total()) # 商品は 2 個登録されています
orange = Product.from_csv_row("みかん,120")
print(orange.name, orange.price) # みかん 120
@classmethod を付けた get_total(cls) を Product.get_total() で呼ぶと、cls に Product クラス自身が自動で入る。cls.total_count でクラス変数にアクセスできる。def total_with_tax(self, tax_rate):- 呼び出し:
apple.total_with_tax(0.1) - →
selfにappleが入る - インスタンス変数 self.price に触れる
@classmethodを付けるdef get_total(cls):- 呼び出し:
Product.get_total()(クラスから) - →
clsにProductが入る - クラス変数 cls.total_count に触れる
スタティックメソッド — @staticmethod
スタティックメソッド は、@staticmethod を付けて定義し、self も cls も受け取りません。引数だけで完結する 「クラスに属しているただの関数」 で、インスタンス変数にもクラス変数にも触れません。
使いどころは、そのクラスに関係はあるけれど、特定のインスタンスやクラスの状態に依存しない計算 — 入力のバリデーション、フォーマット変換、定数を使った純粋な計算など。外側に普通の関数として書く代わりに、関係するクラスの中にまとめておく ためのものです。
class Product:
def __init__(self, name, price):
if not Product.is_valid_price(price):
raise ValueError(f"不正な価格: {price}")
self.name = name
self.price = price
@staticmethod
def is_valid_price(price): # self も cls も無い
return 0 <= price <= 1_000_000
print(Product.is_valid_price(150)) # True
print(Product.is_valid_price(-1)) # False
# Product("りんご", -1) # ValueError: 不正な価格: -1
スタティックメソッドはクラスからもインスタンスからも呼べる
Product.is_valid_price(150) でも apple.is_valid_price(150) でも同じように呼び出せます。が、インスタンスを使わない処理なので クラスから呼ぶ方が意図が伝わります。apple.is_valid_price(...) と書くと、読み手は「apple の状態に依存しているのかな?」と一瞬考えてしまうため、避けたほうが親切です。
使い分けの判断軸
3 種類のうちどれにすべきかは、「メソッドの中で何に触りたいか」 で機械的に決められます。
- インスタンス変数(self.x)に触る → インスタンスメソッド
- クラス変数(cls.x)にだけ触る → クラスメソッド
- どちらにも触らない(引数だけで完結する) → スタティックメソッド
迷ったら、まずインスタンス変数に触っているかを確認します。触っていなければクラス変数に触っているかを確認、それも無ければスタティック、という順で考えると外しません。
self.x に触るかどうかが最初の分岐。次にクラス変数を扱うかどうか。両方触らなければスタティックでよい。| 種類 | デコレータ | 第 1 引数 | 典型用途 |
|---|---|---|---|
| インスタンスメソッド | なし | self | self.x を読み書きする通常のメソッド |
| クラスメソッド | @classmethod | cls | クラス変数の読み書き、ファクトリ |
| スタティックメソッド | @staticmethod | なし | バリデーション、フォーマット変換 |
今回でメソッドの 3 種類 — インスタンスメソッド/クラスメソッド/スタティックメソッド — の違いと使い分けまで一通り押さえました。これで、Python における クラス・インスタンス・属性・メソッド の基本セットがすべて揃ったことになります。ここから先は、カプセル化(属性の公開範囲) や、継承 で別のクラスから機能を引き継ぐ書き方、ポリモーフィズム で同じメソッド名を型ごとに振る舞いを変える書き方など、オブジェクト指向の 3 大要素に踏み込んでいくことになります。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2次のメソッドのうち、@classmethod を付けるのが最も自然 なのはどれですか?
Q3次のうち、インスタンスを作らずに呼び出せる メソッドはどれですか?(複数該当する場合は最も適切な答えを選んでください)