Python

Pythonの@classmethodとは|clsの役割とstaticmethodとの違い

Pythonの@classmethodは、インスタンスを作らずにクラスから直接呼び出せるメソッドを定義するデコレータです。第一引数が受け取るのは、インスタンスを表すselfではなくクラス自身で、慣習的な呼び名がclsです。見た目の差は小さいものの、オブジェクトの生成方法を増やす場面や、継承したサブクラスごとに挙動を変える場面では、この一文字の差が結果を分けます。この記事では、定義の書き方から、インスタンスメソッド・スタティックメソッドとの使い分け、公式ドキュメントが主用途として挙げる代替コンストラクタ、そしてPython 3.13で削除された書き方までを扱います。

まとめ:クラスメソッドの要点と判断基準

  • @classmethodを付けたメソッドは第一引数にクラスオブジェクトを受け取ります。慣習的な名前がclsで、selfとの違いはここにあります。
  • Python公式ドキュメントは@classmethodを「代替コンストラクタ(alternate class constructors)を作るのに有用」と位置づけています。dict.fromkeys()datetime.fromtimestamp()がその実例です。
  • クラス状態を触るならクラスメソッド、インスタンスの値を使うならインスタンスメソッド、どちらも使わないならスタティックメソッド、という順で判断します。
  • サブクラスから呼ぶとclsにはサブクラスが渡ります。return cls()と書くかreturn Base()と書くかで、継承したときの結果が変わります。
  • @classmethod@propertyを重ねてクラスプロパティを作る書き方は、Python 3.11で非推奨となり3.13で削除されました。3.11・3.12では警告すら出ずに動くため、3.13へ上げた瞬間に表面化します。

クラスメソッドの定義と@classmethodデコレータの役割

クラスメソッドは、defの直前に@classmethodを置くだけで定義できます。Python公式ドキュメントは、この書き方を次のイディオムとして示しています。

class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

    @classmethod
    def from_fahrenheit(cls, fahrenheit):
        return cls(round((fahrenheit - 32) / 1.8, 1))


print(Temperature.from_fahrenheit(212).celsius)
# 100.0

通常のメソッドはTemperature(100).method()のようにインスタンス経由でしか呼べませんが、クラスメソッドはクラス名から直接呼べる点が最初の違いです。

先頭の@が意味するデコレータ構文

@classmethodの先頭に付く@は、クラスメソッド専用の記号ではありません。Pythonのデコレータ構文そのもので、直後に定義した関数を、@に続けて書いた呼び出し可能オブジェクトに通してから名前に束縛する、という意味を持ちます。公式ドキュメントも@classmethodを「関数デコレータ」と説明しています。

つまり@classmethodは、組み込みのclassmethod()に関数を渡した結果を、同じ名前で置き直しているだけです。次の2つは同じ意味になります。

class A:
    @classmethod
    def f(cls):
        return cls.__name__


class B:
    def f(cls):
        return cls.__name__
    f = classmethod(f)


print(A.f(), B.f())
# A B

普段は上の書き方を使えば十分です。下の形を知っておくと、@が特別な文法ではなく関数適用の省略記法だと分かり、後述するデコレータの重ね掛けの挙動も理解しやすくなります。

第一引数clsが受け取るクラスオブジェクト

clsには、そのメソッドを呼び出したクラス自身が入ります。インスタンスメソッドのselfがインスタンスを受け取るのに対応する位置づけです。clsという名前は言語仕様ではありませんが、PEP 8が「クラスメソッドの第一引数には常にclsを使うこと」と定めています(インスタンスメソッドのselfも同様)。klassなどに変えても動作はしますが、読み手を迷わせるだけなので従ってください。

クラスメソッドはインスタンスからも呼び出せます。公式ドキュメントは「クラスメソッドはクラス(C.f())からもインスタンス(C().f())からも呼び出せる。インスタンスはそのクラスを除いて無視される」と明記しています。インスタンス経由で呼んでもclsに入るのはクラスであり、そのインスタンス固有の属性には触れません。

インスタンスメソッド・クラスメソッド・スタティックメソッドの違い

Pythonのクラスで定義できるメソッドは3種類あり、違いは第一引数に何が自動で渡るかだけです。次のクラスは3種類を1つにまとめたものです。

class Order:
    tax_rate = 0.1

    def __init__(self, amount):
        self.amount = amount

    def total(self):
        # インスタンスメソッド: self 経由で自分の amount を使う
        return self.amount * (1 + Order.tax_rate)

    @classmethod
    def set_tax_rate(cls, rate):
        # クラスメソッド: cls 経由でクラス変数を書き換える
        cls.tax_rate = rate

    @staticmethod
    def is_valid_amount(value):
        # スタティックメソッド: self も cls も受け取らない
        return isinstance(value, int) and value > 0


print(Order(1000).total())
# 1100.0
Order.set_tax_rate(0.08)
print(Order(1000).total())
# 1080.0
print(Order.is_valid_amount(-5))
# False

3種類のメソッドの第一引数と呼び出し可否の比較

種類 デコレータ 第一引数 触れる対象 インスタンス無しで呼べるか
インスタンスメソッド なし self(インスタンス) インスタンス変数とクラス変数 不可
クラスメソッド @classmethod cls(クラス) クラス変数とクラス自身
スタティックメソッド @staticmethod 自動では渡らない 引数で渡した値のみ

公式ドキュメントは、Pythonのクラスメソッドについて「C++やJavaのスタティックメソッドとは異なる。それが必要ならstaticmethod()を参照」と注意を促しています。他言語の静的メソッドに相当するのは@staticmethodのほうで、@classmethodはクラスを引数として受け取る分だけ機能が多い、と捉えると混同を避けられます。

3種類のメソッドを選ぶ判断順序

判断は「そのメソッドが何を必要とするか」を上から順に見れば決まります。

  • 個々のインスタンスの値(self.amountなど)を使う → インスタンスメソッド
  • インスタンスは不要だがクラス自身(クラス変数、またはcls(...)によるインスタンス生成)を使う → クラスメソッド
  • どちらも使わず、引数だけで完結する → スタティックメソッド

3番目については、そもそもクラスに置くべきかを疑ってください。selfclsも使わない処理は、モジュールレベルの関数として定義したほうが呼び出し側は短く書けます。それでもクラスに置く理由は、名前空間としてクラスに属していたほうが探しやすい場合と、サブクラスでの上書きを許したい場合の2つです。逆に「とりあえずクラスにまとめる」目的で@staticmethodを並べると、クラスが実質的な関数置き場になり、継承もインスタンス化も使われないまま肥大化します。

代替コンストラクタとしてのクラスメソッド

Python公式ドキュメントはstaticmethodの項で、@classmethodを「代替クラスコンストラクタ(alternate class constructors)を作るのに有用な変種」と説明しています。クラスメソッドの用途としては、これが中心です。

__init__は1つのクラスに1つしか定義できません。しかし実務では、辞書から作りたい、CSVの1行から作りたい、APIのレスポンスから作りたい、と生成経路が複数になります。この分岐を__init__の引数で吸収する設計は、引数の増加と型判定の混入を招く悪手です。生成経路ごとにクラスメソッドを用意すれば、__init__は素直な形のまま保てます。

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def from_dict(cls, data):
        return cls(data["name"], data["age"])

    @classmethod
    def from_csv_row(cls, row):
        name, age = row.split(",")
        return cls(name.strip(), int(age))


u1 = User.from_dict({"name": "taro", "age": 30})
u2 = User.from_csv_row("hanako, 25")
print(u1.name, u2.age)
# taro 25

呼び出し側はUser.from_csv_row(row)と書くだけで、何から生成しているかがコードに現れます。生成手段を独立した関数make_user_from_csv()として外に置くこともできますが、その場合Userをインポートしても生成手段は付いてきません。クラスメソッドにしておけば、クラスと生成手段が常に一緒に移動します。

なお、生成対象のクラス自体を実行時に選び分けたい場合は、クラスメソッドではなくPythonにおけるファクトリーメソッドパターンの概要で扱う設計が向きます。代替コンストラクタは「作るクラスは決まっていて入力形式が複数ある」場合、ファクトリーメソッドパターンは「入力に応じて作るクラス自体が変わる」場合、と切り分けてください。

継承時にclsが指すクラス

クラスメソッドの挙動でつまずきやすいのが、継承したときにclsが何を指すかです。公式ドキュメントは「クラスメソッドが派生クラスに対して呼び出された場合、派生クラスのオブジェクトが暗黙の第一引数として渡される」と定めています。

class Base:
    @classmethod
    def create(cls):
        return cls()


class Sub(Base):
    pass


print(Base.create().__class__.__name__)
# Base
print(Sub.create().__class__.__name__)
# Sub

Sub.create()Baseで定義されたメソッドを実行していますが、生成されるのはSubのインスタンスです。ここをreturn Base()と書いてしまうと、サブクラスから呼んでも常にBaseが返り、継承した意味が失われます。前節の代替コンストラクタでreturn cls(...)と書くのは、この差を吸収するためです。

そのため、継承先で挙動を変えたい処理をスタティックメソッドで書くのは避けてください。@staticmethodはクラスを受け取らないので、サブクラスから呼んでも自分がどのクラスとして呼ばれたかを知る手段がありません。「サブクラスごとに結果が変わるべきか」が、両者を分ける実務上の判断軸です。

この性質は、クラス階層の初期化にも使われています。PEP 487で導入された__init_subclass__は、サブクラスが定義された時点で呼ばれるフックです。PEPは「これは暗黙的に@classmethodである」と述べています。デコレータを書かなくてもクラスメソッドとして扱われるため、メタクラスを書かずにサブクラスの登録処理を実装できます。

class Plugin:
    registry = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Plugin.registry.append(cls.__name__)


class CsvPlugin(Plugin):
    pass


class JsonPlugin(Plugin):
    pass


print(Plugin.registry)
# ['CsvPlugin', 'JsonPlugin']

抽象基底クラスで「サブクラスが必ず実装すべきクラスメソッド」を宣言する場合は、@classmethodを上、@abstractmethodを下に重ねます。abcモジュールには@abstractclassmethodもありますが、公式ドキュメントはこれをPython 3.3で非推奨とし、「@classmethod@abstractmethodと併用できるようになったため、このデコレータは冗長になった」と説明しています。新規のコードで使う理由はありません。

クラスメソッドによるクラス変数の管理

クラス変数は全インスタンスで共有される値です。これを読み書きする窓口としてクラスメソッドを置くと、更新の入口が1か所にまとまります。

class Session:
    active = 0

    @classmethod
    def open(cls):
        cls.active += 1
        return cls()

    @classmethod
    def reset(cls):
        cls.active = 0


Session.open()
Session.open()
print(Session.active)
# 2
Session.reset()
print(Session.active)
# 0

注意すべきなのは、インスタンス側から同じ名前に代入した場合です。s = Session(); s.active = 99と書くと、クラス変数は変わらず、そのインスタンスだけに新しいインスタンス変数activeが作られます。以後、s.activeはインスタンス変数を、Session.activeはクラス変数を返し、両者の値はずれたままです。共有カウンタのつもりで実装した箇所でこれが起きると、原因の特定に時間がかかります。更新はcls.activeを触るクラスメソッド経由に限定し、インスタンス経由の代入をしない、という規約で防ぐのが確実です。

また、クラス変数に可変オブジェクト(リストや辞書)を置く場合、それは全インスタンス、さらにサブクラスとも共有されます。前節のPlugin.registryのように意図して共有するなら問題ありませんが、インスタンスごとに独立させたい値をクラス変数に置くのは誤りです。その場合は__init__の中でself.items = []と初期化してください。

標準ライブラリと実務フレームワークでの実例

クラスメソッドは、標準ライブラリの中で代替コンストラクタとして使われています。実際に型を確認すると、次のようにクラスメソッドとして実装されていることが分かります。

import datetime
import pathlib

print(type(dict.__dict__["fromkeys"]).__name__)
# classmethod_descriptor
print(type(datetime.datetime.__dict__["fromtimestamp"]).__name__)
# classmethod_descriptor
print(type(int.__dict__["from_bytes"]).__name__)
# classmethod_descriptor
print(type(pathlib.Path.__dict__["cwd"]).__name__)
# classmethod

dict.fromkeys()datetime.fromtimestamp()datetime.fromisoformat()int.from_bytes()はいずれも、既存の値から新しいインスタンスを作る代替コンストラクタです。fromkeysfrom_bytesという命名がそのまま示しているとおり、「何から作るか」を名前に含めるのが標準ライブラリの流儀になっています。自分で代替コンストラクタを書くときもfrom_で始める名前に合わせておくと、読み手は中身を見る前に用途を判断できます。

pathlibPath.cwd()Path.home()も同じくクラスメソッドです。引数なしで環境から情報を取り、Pathインスタンスを返すという点で、こちらも代替コンストラクタにあたります。実際の使い方はPython pathlibの使い方|resolve・絶対パス取得・Path(__file__).resolve().parentとos.pathの違いで扱っています。

フレームワークでも同様です。Pydantic v2の概要と特徴:V1からの進化点と性能向上で扱うPydantic v2では、フィールド検証を書く@field_validatorクラスメソッドに適用することが公式ドキュメントで推奨されています。@classmethodを省いても検証自体は動きますが、型チェッカーが第一引数をインスタンスと誤認します。

実害があるのは順序のほうです。@field_validatorを上、@classmethodを下に置きます。逆順にすると例外も警告も出ないまま検証が無効化され、値が加工されずに素通りします(pydantic 2.13.4で確認)。

from pydantic import BaseModel, field_validator


class User(BaseModel):
    name: str

    @field_validator("name", mode="after")
    @classmethod
    def strip_name(cls, value: str) -> str:
        return value.strip()

検証関数がインスタンスではなくクラスを受け取るのは、値の検証がインスタンス生成の途中で走るためです。まだ完成していないインスタンスにselfで触れないので、クラスメソッドとして定義する必要があります。

バージョンで変わったclassmethodの仕様

クラスメソッドの仕様は、3.9以降だけでも複数回変わっています。移行時に影響が出るのは次の2点です。

Python 3.13で削除されたpropertyとの重ね掛け

最も注意すべきバージョン差は、@classmethod@propertyを重ねる書き方です。次のコードは、括弧なしでConfig.nameと書けるクラスプロパティを作る目的で使われてきました。

class Config:
    _name = "prod"

    @classmethod
    @property
    def name(cls):
        return cls._name

この書き方はPython 3.9で導入されました。公式ドキュメントは3.9の変更として「クラスメソッドはproperty()などの他のデスクリプタをラップできるようになった」と記載しています。ところが同じ項目には、続けて次の記載があります。「バージョン3.11で非推奨、バージョン3.13で削除:クラスメソッドはproperty()などの他のデスクリプタをラップできなくなりました」。

つまりPython 3.9で追加され、3.11でドキュメント上の非推奨とされ、Python 3.13の新機能まとめ|JIT・フリースレッド・対話型シェルの要点と3.14との違い【2026年版】で扱う3.13で機能自体が削除された書き方です。やっかいなのは、3.11と3.12では実行してもDeprecationWarningが一切出ず、そのまま正しく動いてしまう点にあります。-X devで開発モードの警告フィルタを有効にしても、-W error::DeprecationWarningで警告を例外に昇格させても、3.11・3.12は何も出さずに素通りします。実行時の警告を頼りに移行対象を洗い出すことはできません。

3.13以降で上のコードを実行しても例外にはなりません。Config.nameは文字列'prod'ではなく<bound method name of <class '__main__.Config'>>というメソッドオブジェクトを返します(クラス名の前に付くのは定義元のモジュール名です)。クラス定義時にも属性アクセス時にも例外が出ないため、f文字列やログ出力に混ぜている箇所はそのまま素通りし、Config.name.upper()のように文字列として扱って初めてAttributeError: 'property' object has no attribute 'upper'で落ちます。壊れた箇所と落ちる箇所が離れる分、移行時の原因追跡は厄介です。

書き換えは、括弧を許容してクラスメソッドに戻すのが最も単純です。

class Config:
    _name = "prod"

    @classmethod
    def name(cls):
        return cls._name


print(Config.name())
# prod

属性としての見た目をどうしても保ちたい場合は、メタクラス側に@propertyを定義する方法が残っていますが、メタクラスの導入コストに見合う場面は多くありません。Python 3.14の新機能まとめ|JITコンパイラ・フリースレッド版(GIL撤廃)・t-stringを最新版で解説で扱う3.14でもこの削除は維持されているため、古い記事やAIの生成コードに残る@classmethod@propertyの重ね掛けは、そのまま採用しないでください。

Python 3.10で追加されたメソッド属性の継承とデスクリプタの呼び出し

Python 3.10では、クラスメソッドとスタティックメソッドがメソッド属性(__module____name____qualname____doc____annotations__)を継承し、__wrapped__属性を持つようになりました。デコレータやドキュメント生成ツールが元の関数名やdocstringを取得できるようになった変更です。3.9以前は__doc__以外の4属性と__wrapped__が欠落し、__doc__も関数のdocstringではなくclassmethod型自身の説明文(「classmethod(function) -> method…」)を返していました。属性が無いのではなく別物が返るため、ドキュメント生成ツール側では例外にならず誤った説明文が出力されます。

同じ3.10で、スタティックメソッドのデスクリプタ自体も呼び出し可能になりました。3.9ではC.__dict__["f"]()が「’staticmethod’ object is not callable」で失敗しますが、3.10以降は成功します。公式ドキュメントが挙げるとおり、クラス定義の内側でf()と書いて定数の初期化などに使えるようになった、という変更です。

よくある質問

clsとselfの違いは何ですか

selfにはインスタンスが、clsにはクラスが渡ります。どちらも言語が予約した名前ではなく、PEP 8が「常にselfを使う」「常にclsを使う」と定めた命名規約です。selfからはインスタンス変数とクラス変数の両方に届きますが、clsから届くのはクラス変数だけです。インスタンス固有の値を読む必要があるかどうかが、両者を選ぶ分かれ目になります。

@classmethodの@はどういう意味ですか

デコレータ構文を表すPythonの記号で、クラスメソッド専用のものではありません。@classmethodと書くと、直後に定義した関数を組み込みのclassmethod()に通した結果が同じ名前に束縛されます。@staticmethod@property、フレームワークが提供するデコレータも同じ仕組みで動きます。複数を重ねた場合は下から順に適用されるため、並べる順序が結果を変えます。

クラスメソッドとインスタンスメソッドはどちらを使うべきですか

インスタンスの状態を読み書きするならインスタンスメソッド、クラス変数の更新やインスタンスの生成そのものを担うならクラスメソッドです。迷ったときはインスタンスメソッドを既定にしてください。クラスメソッドはselfを持たない分だけ扱える情報が少なく、後からインスタンスの値が必要になると書き換えが発生します。

スタティックメソッドとクラスメソッドはどう使い分けますか

clsを使うかどうかで決まります。クラス変数を触る、あるいはcls(...)でインスタンスを作るならクラスメソッドです。引数だけで完結する処理はスタティックメソッドですが、その場合はモジュールレベルの関数で足りないかを先に検討してください。他言語の静的メソッドに対応するのは@staticmethodのほうです。

クラスメソッドはインスタンスから呼び出せますか

呼び出せます。公式ドキュメントも、クラスからでもインスタンスからでも呼べると明記しています。ただしインスタンス経由で呼んでもclsに入るのはクラスであり、そのインスタンス固有の属性は参照できません。混乱を避けるため、コード上はクラス名から呼ぶ形に統一しておくと読み手に意図が伝わります。

関連記事

資料請求

RELATED POSTS 関連記事