順番に読み進めながら学べます

インスタンス・クラス・スタティックメソッド — 3 種類のメソッド

Python のメソッドの 3 種類 — インスタンス・クラス・スタティック — の違いを図解で解説します。self / cls / 引数なしの使い分けまで押さえます。

前回まででクラス変数とインスタンス変数の違いまで押さえてきました。ここまで書いてきた def method(self, ...) 形式のメソッドはすべて インスタンスメソッド ですが、Python のクラスにはあと 2 種類のメソッド — クラスメソッドスタティックメソッド — があります。今回は 3 種類の違いと使い分けの判断軸を整理します。

メソッドは 3 種類ある

Python のクラスに定義できるメソッドは、第 1 引数に何を取るかで 3 種類に分かれます。

- インスタンスメソッド — 第 1 引数 selfインスタンス自身を受け取る

- クラスメソッド — 第 1 引数 clsクラス自身を受け取る。@classmethod を付ける

- スタティックメソッド — 第 1 引数なし。selfcls も受け取らない。@staticmethod を付ける

どの種類かによって、何にアクセスできるか が決まります。インスタンス変数 self.x に触れるのはインスタンスメソッドだけです。クラス変数を クラス側から使うのがクラスメソッド、どちらにも触れずに純粋な処理だけ書くのがスタティックメソッドです。

3 種類のメソッドが扱える範囲
インスタンスメソッド (self)インスタンス変数+ クラス変数+ 引数クラスメソッド (cls)クラス変数+ 引数スタティックメソッド (なし)引数のみ扱える扱える扱える
インスタンスメソッドは インスタンス変数 + クラス変数 + 引数 すべてに、クラスメソッドは クラス変数 + 引数 に、スタティックメソッドは 引数のみ に触れる。

インスタンスメソッド — 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
self を通じてインスタンス変数にアクセスする流れ
__init__self.price = 150appleprice = 150apple.total_with_tax(0.1)self にapple が入るself.price→ 150①設定②呼出③参照
__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を付けるdef get_total( cls):Product.get_total()cls = Product(クラス自身)cls.total_count(クラス変数)宣言自動経由参照
@classmethod を付けた get_total(cls)Product.get_total() で呼ぶと、cls に Product クラス自身が自動で入るcls.total_count でクラス変数にアクセスできる。
self を取るか、cls を取るか
インスタンスメソッド
  • def total_with_tax(self, tax_rate):
  • 呼び出し: apple.total_with_tax(0.1)
  • selfapple が入る
  • インスタンス変数 self.price に触れる
クラスメソッド
  • @classmethod を付ける
  • def get_total(cls):
  • 呼び出し: Product.get_total() (クラスから)
  • clsProduct が入る
  • クラス変数 cls.total_count に触れる
self は「呼び出したインスタンス」、cls は「そのクラス自身」。クラス変数だけ触りたい処理は cls 経由のほうが意図が伝わる。

クラスメソッドで「これまでに作られた商品の数」を返す get_total を書きます。

class Product: の中にクラス変数 total_count = 0 を書いてください。

__init__(self, name, price)self.name = name / self.price = price を代入したあと、Product.total_count += 1 を書いてください。

get_total@classmethod 付きで定義し、第 1 引数を cls にして return cls.total_count を返してください。

Product("りんご", 150) / Product("バナナ", 80) を作り、print(Product.get_total())2 が表示されることを確認してください。

(正しく実行できれば解説が表示されます)

Python エディタ

コードを実行してください

スタティックメソッド — @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 の状態に依存しているのかな?」と一瞬考えてしまうため、避けたほうが親切です。

価格のバリデーション をスタティックメソッドで書きます。

class Product: の中に @staticmethodis_valid_price(price) を定義し、return 0 <= price <= 1_000_000 を返してください。

__init__(self, name, price) の冒頭で if not Product.is_valid_price(price): raise ValueError("不正な価格") を書いてから、self.name = name / self.price = price を代入してください。

Product("りんご", 150) は成功すること、Product("うに", -1)ValueError で止まることを try / except で確認してください。

Python エディタ

コードを実行してください

使い分けの判断軸

3 種類のうちどれにすべきかは、「メソッドの中で何に触りたいか」 で機械的に決められます。

- インスタンス変数(self.x)に触るインスタンスメソッド

- クラス変数(cls.x)にだけ触るクラスメソッド

- どちらにも触らない(引数だけで完結する)スタティックメソッド

迷ったら、まずインスタンス変数に触っているかを確認します。触っていなければクラス変数に触っているかを確認、それも無ければスタティック、という順で考えると外しません。

メソッド種別の判断フロー
self.x触る?インスタンスメソッドcls.x触る?クラスメソッドどちらも触らないスタティックメソッドクラスに属する純粋な計算用YesNoYesNo用途
インスタンス変数 self.x に触るかどうかが最初の分岐。次にクラス変数を扱うかどうか。両方触らなければスタティックでよい。
種類デコレータ第 1 引数典型用途
インスタンスメソッドなしselfself.x を読み書きする通常のメソッド
クラスメソッド@classmethodclsクラス変数の読み書き、ファクトリ
スタティックメソッド@staticmethodなしバリデーション、フォーマット変換

3 種類すべてを 1 つのクラスに同居させて使い分けを体感します。

class Product: を定義し、クラス変数 total_count = 0__init__(self, name, price) を書いてください。__init__ の冒頭で Product.is_valid_price(price) がエラーなら ValueError を出し、OK なら self.name = name / self.price = price を代入し Product.total_count += 1 を実行してください。

② インスタンスメソッド total_with_tax(self, tax_rate) を作り、return int(self.price * (1 + tax_rate)) を返してください。

③ クラスメソッド get_total(cls)@classmethod で定義し、return cls.total_count を返してください。

④ スタティックメソッド is_valid_price(price)@staticmethod で定義し、return 0 <= price <= 1_000_000 を返してください。

apple = Product("りんご", 150) を作り、apple.total_with_tax(0.1)Product.get_total() の 2 つの結果を print してください。

Python エディタ

コードを実行してください

今回でメソッドの 3 種類 — インスタンスメソッド/クラスメソッド/スタティックメソッド — の違いと使い分けまで一通り押さえました。これで、Python における クラス・インスタンス・属性・メソッド の基本セットがすべて揃ったことになります。ここから先は、カプセル化(属性の公開範囲) や、継承 で別のクラスから機能を引き継ぐ書き方、ポリモーフィズム で同じメソッド名を型ごとに振る舞いを変える書き方など、オブジェクト指向の 3 大要素に踏み込んでいくことになります。

QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1スタティックメソッド を定義するときに付けるデコレータはどれですか?

Q2次のメソッドのうち、@classmethod を付けるのが最も自然 なのはどれですか?

Q3次のうち、インスタンスを作らずに呼び出せる メソッドはどれですか?(複数該当する場合は最も適切な答えを選んでください)