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

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

Pythonのインスタンス・クラス・スタティックメソッドの違いをProductクラスで解説。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次のうち、インスタンスを作らずに呼び出せるメソッドはどれですか?(複数該当する場合は最も適切な答えを選んでください)