- Python
Python classmethodとstaticmethodの違いと使い分け
classmethod(クラスメソッド)とstaticmethod(スタティックメソッド)の違いは、「第1引数に何が自動で渡されるか」です。 インスタンスメソッドにはインスタンス(self)、classmethodにはクラス(cls)が渡され、staticmethodには何も渡されません。この1点から、触れられる変数、継承したときの動き、向いている用途がすべて決まります。
この記事では、3種類のメソッドを1枚の早見表で比べたうえで、self やデコレータを付け忘れたときの TypeError をメッセージ全文つきで再現します。さらに、継承したときに cls が子クラスを指す様子、typing.Self(Python 3.11以降)を使った from_〜 形式のファクトリメソッド、「そもそもモジュール関数で足りるのでは」という判断基準まで、Python 3.10.18/3.11.15/3.14.7で実行した結果とともにまとめました。
3種類のメソッドの早見表
まずこの表で全体像をつかんでください。 以降の章は、この表の各行を実行結果で確かめていく構成です。
| 比べる点 | インスタンスメソッド | classmethod | staticmethod |
|---|---|---|---|
| デコレータ | なし | @classmethod |
@staticmethod |
| 第1引数 | self(インスタンス) |
cls(クラス) |
なし |
| クラス変数に触れるか | 触れる(self.x で読む/type(self).x で書く) |
触れる(cls.x で読み書き) |
クラス名を直接書けば触れる |
| インスタンス変数に触れるか | 触れる(self.x) |
触れない | 触れない |
| クラスから呼ぶ | クラス.m(インスタンス) と渡す必要あり |
クラス.m() |
クラス.m() |
| インスタンスから呼ぶ | obj.m() |
obj.m()(cls はそのクラス) |
obj.m() |
| 継承時の挙動 | self は実際のインスタンス |
cls は呼び出した子クラス |
何も渡らない。子で上書きは可能 |
| 主な用途 | そのオブジェクトの状態を読む・変える | 別の作り方(from_〜)、クラス単位の設定や集計 |
クラスに関係する補助的な計算・判定 |
選び方は、メソッドの中で何を使うかで決まります。
| メソッドの中で使うもの | 選ぶもの |
|---|---|
self.〜(そのインスタンスの値) |
インスタンスメソッド |
self は使わないが、クラス自身(cls(...) で生成、cls.変数) |
classmethod |
self も cls も使わず、引数だけで完結 |
staticmethod、またはモジュール関数 |
付け忘れで出るエラーの対応表も先に載せます(すべて今回の実行結果)。
| エラーメッセージ | 原因 |
|---|---|
TypeError: A.no_self() takes 0 positional arguments but 1 was given |
def に self を書き忘れた |
TypeError: A.greet() missing 1 required positional argument: 'self' |
インスタンスを作らずに クラス.メソッド() と呼んだ |
TypeError: A.make() missing 1 required positional argument: 'name' |
@classmethod を付け忘れた |
TypeError: A.helper() takes 1 positional argument but 2 were given |
@staticmethod を付け忘れてインスタンスから呼んだ |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python classmethodとstaticmethodの違いと使い分け」
確認した環境
| 項目 | 内容 |
|---|---|
| OS | Linux(コンテナ) |
| 主に使った版 | Python 3.11.15 |
| 比べた版 | Python 3.10.18/3.14.7 |
| 使ったもの | 標準ライブラリのみ(typing、json、datetime、timeit、abc) |
とくに断りのない出力は Python 3.11.15 のものです。0x7f... のようなアドレスは実行のたびに変わります。トレースバック内のファイルパスは短い名前に置き換えています。
3種類を1つのクラスで動かして比べる
まずは3種類を1つのクラスに並べた、貼って動くコードです。
class Counter:
total = 0 # クラス変数(全インスタンス共通)
def __init__(self, name):
self.name = name # インスタンス変数
self.count = 0
def add(self): # インスタンスメソッド
self.count += 1
Counter.total += 1
return f"{self.name}: {self.count}"
@classmethod
def report(cls): # クラスメソッド
return f"{cls.__name__} 全体で {cls.total} 回"
@staticmethod
def is_valid_name(name): # スタティックメソッド
return name.isalpha() and len(name) <= 10
a = Counter("alpha")
b = Counter("beta")
print(a.add()); print(a.add()); print(b.add())
print(Counter.report()) # クラスから
print(a.report()) # インスタンスからでも呼べる
print(Counter.is_valid_name("gamma"))
print(a.is_valid_name("no good"))
print(Counter.add)
print(a.add)
print(Counter.report)
print(Counter.is_valid_name)
実行結果:
alpha: 1
alpha: 2
beta: 1
Counter 全体で 3 回
Counter 全体で 3 回
True
False
<function Counter.add at 0x7f14d8519800>
<bound method Counter.add of <__main__.Counter object at 0x7f14d852a250>>
<bound method Counter.report of <class '__main__.Counter'>>
<function Counter.is_valid_name at 0x7f14d8519940>
後半の4行が、3種類の違いをそのまま表しています。
| 取り出し方 | 表示 | 意味 |
|---|---|---|
Counter.add |
function |
クラスから取り出すと、ただの関数。self は自分で渡す |
a.add |
bound method ... of <Counter object> |
インスタンスが結び付いたメソッド。self が自動で入る |
Counter.report |
bound method ... of <class 'Counter'> |
クラスが結び付いたメソッド。cls が自動で入る |
Counter.is_valid_name |
function |
何も結び付かない、ただの関数 |
「bound method(束縛メソッド)」とは、第1引数がすでに埋まった状態のメソッドのことです。何が結び付くか(インスタンスか、クラスか、何もなしか)が、3種類の違いのすべてです。
インスタンスメソッド:selfでそのオブジェクトの値を扱う
デコレータを付けずにクラスの中で def すると、インスタンスメソッドになります。もっともよく使う形です。
a.add() という呼び出しは、内部では Counter.add(a) と同じ意味になります。第1引数の self に、ドットの左側のインスタンス a が入ります。手元でも k.m(1) == K.m(k, 1) が True になることを確かめました。
| 項目 | 内容 |
|---|---|
| 使う場面 | インスタンスごとに違う値(self.name など)を読んだり変えたりする |
| 呼び出し方 | obj.method() |
| 第1引数の名前 | 慣習で self(文法上は別名でも動くが、self にそろえる) |
self.変数 += 1 と書いたときのクラス変数の動き
インスタンスメソッドからクラス変数を書き換えるときは注意が要ります。self.total += 1 と書くと、クラス変数ではなく、そのインスタンスに新しい変数が作られます。
class Hit:
total = 0
def add(self):
self.total += 1 # インスタンス変数が新しくできる
@classmethod
def add_cls(cls):
cls.total += 1
h = Hit(); h.add(); h.add()
print(Hit.total, h.total, h.__dict__)
Hit.add_cls()
print(Hit.total, h.total)
0 2 {'total': 2}
1 2
h.add() を2回呼んでも Hit.total は 0 のままで、h の中に total が作られています。クラス全体で共有する値を変えたいときは classmethod で cls.total += 1 と書くと、意図どおりクラス変数が更新されます。
classmethod:clsでクラスそのものを扱う
@classmethod を付けると、第1引数にクラス自身が渡されます。名前は慣習で cls です。インスタンスを作らなくても クラス.メソッド() で呼べます。
classmethodの主な用途は2つです。
| 用途 | 内容 | 例 |
|---|---|---|
| 別の作り方を用意する(ファクトリメソッド) | __init__ とは違う入力からインスタンスを作る |
from_dict、from_json、from_csv_row |
| クラス単位の値を扱う | クラス変数の読み書き、設定の切り替え、生成数の集計 | report()、configure() |
標準ライブラリにも多くの例があります。dict.fromkeys(...)、datetime.date.fromisoformat(...)、int.from_bytes(...) はどれもclassmethodで、「クラスから呼ぶと、そのクラスのインスタンスが返る」形です。
from_〜形式のファクトリメソッドをtyping.Self付きで書く
__init__ は1つしか書けませんが、classmethodを使えば入力の形ごとに作り方を増やせます。戻り値の型ヒントには、Python 3.11で追加された typing.Self が使えます。
from __future__ import annotations
import json
from datetime import date
from typing import Self # Python 3.11 以降
class Task:
def __init__(self, title: str, due: date, done: bool = False) -> None:
self.title = title
self.due = due
self.done = done
def __repr__(self) -> str:
return f"{type(self).__name__}(title={self.title!r}, due={self.due.isoformat()}, done={self.done})"
@classmethod
def from_dict(cls, data: dict) -> Self:
return cls(data["title"], date.fromisoformat(data["due"]), data.get("done", False))
@classmethod
def from_json(cls, text: str) -> Self:
return cls.from_dict(json.loads(text))
@classmethod
def from_csv_row(cls, row: str) -> Self:
title, due, done = row.split(",")
return cls(title, date.fromisoformat(due), done == "1")
@classmethod
def today(cls, title: str) -> Self:
return cls(title, date(2026, 10, 3)) # 記事用に固定。通常は date.today()
class UrgentTask(Task):
def label(self) -> str:
return f"【至急】{self.title}"
print(Task.from_dict({"title": "見積もり作成", "due": "2026-10-10"}))
print(Task.from_json('{"title": "備品発注", "due": "2026-10-05", "done": true}'))
print(Task.from_csv_row("会議室予約,2026-10-07,0"))
u = UrgentTask.from_csv_row("資料確認,2026-10-04,0")
print(u, "->", u.label())
print(UrgentTask.today("棚卸し"))
Task(title='見積もり作成', due=2026-10-10, done=False)
Task(title='備品発注', due=2026-10-05, done=True)
Task(title='会議室予約', due=2026-10-07, done=False)
UrgentTask(title='資料確認', due=2026-10-04, done=False) -> 【至急】資料確認
UrgentTask(title='棚卸し', due=2026-10-03, done=False)
ポイントは3つです。
Task(...)ではなくcls(...)で作る。こうしておくと、子クラスUrgentTaskから呼んだときにUrgentTaskのインスタンスが返り、label()も使えるfrom_jsonからcls.from_dict(...)を呼ぶように、classmethod同士はcls.でつなぐと、子クラスでの上書きも反映される- 戻り値を
-> Selfと書くと、型チェッカーが「UrgentTask.from_csv_row(...)の戻り値はUrgentTask」と読み取れる
typing.Selfとクラス名を書く方法の違い
| 書き方 | 子クラスから呼んだときの型の読まれ方 | 使える版 |
|---|---|---|
-> Self |
呼び出した子クラス | Python 3.11以降(from typing import Self) |
-> "Task" |
いつも Task |
すべての版 |
TypeVar を自分で定義して cls: type[T] と -> T |
呼び出した子クラス | 3.10以前でも可 |
Python 3.10.18で上のコードを動かすと、import の時点で次のように止まりました。
ImportError: cannot import name 'Self' from 'typing' (.../lib/python3.10/typing.py)
3.10以前でも同じ書き方をしたいときは、外部パッケージ typing_extensions から Self を読み込む方法が知られています(今回は未実行)。
公式ドキュメントの typing のページ(今回開いて確認)には、Self が3.11で追加されたこと、classmethodを別のコンストラクタとして使う例、そして子クラスにしたときに子クラスのインスタンスが返るとは限らないメソッドには Self を使わないという注意が書かれています。つまり、メソッドの中で Task(...) とクラス名を直接書いているなら -> "Task"、cls(...) で作っているなら -> Self が対応する書き方です。型チェッカー(mypy や pyright)での確認は今回は未実行です。
staticmethod:selfもclsも使わない補助的な処理
@staticmethod を付けたメソッドには、何も自動では渡されません。中身はふつうの関数と同じで、置き場所がクラスの中にあるという点だけが違います。
class Counter:
@staticmethod
def is_valid_name(name):
return name.isalpha() and len(name) <= 10
向いているのは、次のような処理です。
| 向いている処理 | 例 |
|---|---|
| そのクラスに関係する入力の判定 | 名前や書式が条件を満たすか |
| 単位や書式の変換 | 文字列を整える、秒を「時:分」にする |
| 子クラスで差し替えたい、状態を持たない処理 | 既定の表示文字列、既定の区切り文字 |
3つめは見落とされがちな使い方です。staticmethodはクラスの属性なので、子クラスで上書きでき、self.メソッド() や cls.メソッド() 経由で呼べば子の実装が使われます(次の章で実行します)。
継承したときの違い:clsは子クラスを指す
classmethodとstaticmethodの差がもっともはっきり出るのが継承です。
class Animal:
kind = "動物"
created = 0
def __init__(self, name):
self.name = name
@classmethod
def create(cls, name):
cls.created += 1
print(f" cls = {cls.__name__}")
return cls(name)
@classmethod
def describe(cls):
return f"{cls.__name__} は {cls.kind}"
@staticmethod
def create_fixed(name):
return Animal(name) # クラス名を直接書いている
@staticmethod
def sound():
return "..."
def speak(self):
return f"{self.name}: {self.sound()}"
class Dog(Animal):
kind = "犬"
@staticmethod
def sound():
return "ワン"
d = Dog.create("ポチ")
print(type(d).__name__, d.speak())
print(Dog.describe(), "/", Animal.describe())
e = Dog.create_fixed("ハチ")
print(type(e).__name__, e.speak())
d2 = d.create("シロ") # インスタンスから呼んでも cls は Dog
print(type(d2).__name__)
print(Animal.created, Dog.created, "created" in Dog.__dict__)
Animal.create("x")
print(Animal.created, Dog.created)
cls = Dog
Dog ポチ: ワン
Dog は 犬 / Animal は 動物
Animal ハチ: ...
cls = Dog
Dog
0 2 True
cls = Animal
1 2
読み取れることを整理します。
| 行 | 起きたこと |
|---|---|
Dog.create("ポチ") |
cls は Dog。親に書いたclassmethodでも、呼び出したクラスが渡るので Dog のインスタンスができる |
Dog.describe() |
cls.kind が Dog.kind(犬)を読む |
Dog.create_fixed("ハチ") |
staticmethodの中で Animal(name) と書いているため、Dog から呼んでも Animal ができる |
d.create("シロ") |
インスタンスから呼んでも、cls にはそのインスタンスのクラス(Dog)が渡る |
self.sound() |
子で上書きしたstaticmethod(ワン)が使われる |
0 2 True |
cls.created += 1 は Dog 側に新しいクラス変数を作る。親の Animal.created は増えない |
最後の行は覚えておきたい挙動です。cls.created += 1 は「cls.created を読んで1足し、cls.created に代入する」という意味なので、cls が Dog なら Dog.created が作られます。親子すべてで1つのカウンターを共有したいなら Animal.created += 1 とクラス名を直接書き、クラスごとに数えたいなら cls.created += 1 と書き分けます。
| 目的 | 書き方 |
|---|---|
| 子クラスから呼んだら子クラスを作りたい | classmethodで cls(...) |
| いつも決まったクラスを作りたい | クラス名を直接書く(staticmethodでもclassmethodでも可) |
| クラスごとに別々の値を持ちたい | cls.変数 |
| 親子で1つの値を共有したい | 親クラス名.変数 |
付け忘れで出るTypeErrorを全文で再現する
self やデコレータを付け忘れたときのエラーは、メッセージの「数」が手がかりになります。8つのパターンを1つのファイルで再現します。
def show(label, f):
try:
f()
except Exception as e:
print(f"[{label}] {type(e).__name__}: {e}")
class A:
def no_self(): # 1. self の書き忘れ
return "hi"
def greet(self):
return "hello"
def make(cls, name): # 3. @classmethod の付け忘れ
return cls()
def helper(x): # 4. @staticmethod の付け忘れ
return x * 2
@classmethod
def from_x(cls, x):
return cls()
@staticmethod
def with_self(self, x): # 5. staticmethod なのに self を書いた
return x
@classmethod
def uses_self(cls):
return cls.greet() # 6. classmethod からインスタンスメソッドを呼ぶ
@staticmethod
def uses_name():
return helper(3) # 7. クラス名なしで呼ぶ
a = A()
show("1 selfの書き忘れ", lambda: a.no_self())
show("2 インスタンスを作らずに呼ぶ", lambda: A.greet())
show("3 @classmethod の付け忘れ", lambda: A.make("x"))
show("4 @staticmethod の付け忘れ", lambda: a.helper(3))
print(" クラスから呼ぶと動く:", A.helper(3))
show("5 staticmethod に self", lambda: a.with_self(3))
show("6 classmethod から self が要るメソッド", lambda: A.uses_self())
show("7 クラス名なしで呼ぶ", lambda: A.uses_name())
show("8 引数の数が多い", lambda: A.from_x(1, 2))
実行結果(Python 3.10.18/3.11.15/3.14.7 の3つで同じ文でした):
[1 selfの書き忘れ] TypeError: A.no_self() takes 0 positional arguments but 1 was given
[2 インスタンスを作らずに呼ぶ] TypeError: A.greet() missing 1 required positional argument: 'self'
[3 @classmethod の付け忘れ] TypeError: A.make() missing 1 required positional argument: 'name'
[4 @staticmethod の付け忘れ] TypeError: A.helper() takes 1 positional argument but 2 were given
クラスから呼ぶと動く: 6
[5 staticmethod に self] TypeError: A.with_self() missing 1 required positional argument: 'x'
[6 classmethod から self が要るメソッド] TypeError: A.greet() missing 1 required positional argument: 'self'
[7 クラス名なしで呼ぶ] NameError: name 'helper' is not defined
[8 引数の数が多い] TypeError: A.from_x() takes 2 positional arguments but 3 were given
try を外したときのトレースバックは次のとおりです(1番のパターン、Python 3.14.7)。
Traceback (most recent call last):
File "sample.py", line 5, in <module>
A().no_self()
~~~~~~~~~~~^^
TypeError: A.no_self() takes 0 positional arguments but 1 was given
メッセージの読み方と直し方
| 番号 | メッセージの要点 | なぜそうなるか | 直し方 |
|---|---|---|---|
| 1 | takes 0 ... but 1 was given |
引数なしで呼んだのに「1個渡された」=インスタンスが自動で渡っている | def no_self(self): と書く |
| 2 | missing ... 'self' |
クラスから直接呼んだので self に入るものがない |
A().greet() とインスタンスから呼ぶ |
| 3 | missing ... 'name' |
デコレータが無いので "x" が cls の位置に入り、name が足りない |
@classmethod を付ける |
| 4 | takes 1 ... but 2 were given |
インスタンスと 3 の2個が渡った |
@staticmethod を付ける |
| 5 | missing ... 'x' |
staticmethodは何も渡さないので、3 が self に入り x が足りない |
引数から self を外す |
| 6 | missing ... 'self' |
cls.greet() はクラスから呼んでいるのと同じ |
インスタンスが要る処理はインスタンスメソッドに置く |
| 7 | NameError: name 'helper' is not defined |
クラスの中の名前は、メソッド内から裸の名前では見えない | A.helper(3)、またはclassmethodにして cls.helper(3) |
| 8 | takes 2 ... but 3 were given |
cls も数に入るので、表示は実際に書いた数より1多い |
呼び出し側の引数を見直す |
「given の数が、自分で渡した数より1つ多い」ときは、self か cls が自動で渡されていると読むと、原因にすぐたどり着けます。
4番は少し紛らわしい例です。@staticmethod を付け忘れても A.helper(3) とクラスから呼ぶぶんには動いてしまい、インスタンスから呼んだときに初めてエラーになります。クラスからもインスタンスからも同じように呼べるようにするのが @staticmethod の役割です。
モジュール関数で足りる場合の判断基準
staticmethodは「クラスの中に置いた関数」なので、クラスの外に置いたモジュール関数でも同じことができます。どちらにするかは、次の基準で決められます。
def to_snake(text): # モジュール関数
return text.strip().lower().replace(" ", "_")
class Text:
PREFIX = "col_"
@staticmethod
def to_snake(text):
return text.strip().lower().replace(" ", "_")
@classmethod
def to_column(cls, text):
return cls.PREFIX + cls.to_snake(text)
class Table(Text):
PREFIX = "tbl_"
print(to_snake(" Order Date "), Text.to_snake(" Order Date "),
Text.to_column("Order Date"), Table.to_column("Order Date"))
order_date order_date col_order_date tbl_order_date
| 判断の質問 | はい | いいえ |
|---|---|---|
| そのクラス以外からも広く使う処理か | モジュール関数 | 次へ |
| 子クラスで差し替えたいか | staticmethod(cls./self. 経由で呼ぶ) |
次へ |
クラス変数や cls(...) を使うか |
classmethod | 次へ |
| 「このクラスの道具」として名前でまとめたいか | staticmethod | モジュール関数 |
flowchart TD
A[メソッドを書く] --> B{self を使う?}
B -- はい --> C[インスタンスメソッド]
B -- いいえ --> D{cls を使う? 生成やクラス変数}
D -- はい --> E[classmethod]
D -- いいえ --> F{子クラスで差し替える または クラスの道具としてまとめたい?}
F -- はい --> G[staticmethod]
F -- いいえ --> H[モジュール関数]
Pythonではファイル(モジュール)自体が名前のまとまりになるので、「クラスに属している必然性」が薄い処理はモジュール関数で十分です。逆に、Text.to_snake のように「どのクラスの道具か」が名前から伝わることに価値があるなら、staticmethodにする意味があります。
呼び出しにかかる時間の違い(参考)
呼び出しの速さも測りました。同じ処理を200万回呼び、3回のうち最も短い時間を1回あたりに直した値です(Python 3.11.15)。
| 呼び方 | 1回あたり |
|---|---|
| モジュール関数 | 97.9 ns |
| staticmethod(クラスから) | 111.1 ns |
| staticmethod(インスタンスから) | 111.6 ns |
| インスタンスメソッド | 98.6 ns |
classmethod(中でstaticmethodを呼ぶ to_column) |
185.8 ns |
classmethodの行は、中でもう1回メソッドを呼んで文字列をつなぐ処理なので、単純な比較にはなりません。それ以外はナノ秒単位のわずかな差で、数値は環境や実行のたびに変わります。Python 3.14.7でも同じ傾向(102.3/115.9/110.4/101.0 ns)でした。速さではなく、読みやすさと設計の意図で選べば十分です。
知っておくと役立つ細かい仕様
デコレータを重ねるときの順番
抽象メソッドと組み合わせるときは、@classmethod を外側(上)に書きます。
from abc import ABC, abstractmethod
class Shape(ABC):
@classmethod
@abstractmethod
def unit(cls): ...
try:
Shape()
except TypeError as e:
print("TypeError:", e)
Python 3.11.15:
TypeError: Can't instantiate abstract class Shape with abstract method unit
Python 3.14.7:
TypeError: Can't instantiate abstract class Shape without an implementation for abstract method 'unit'
classmethodとpropertyの重ね書きは版で動きが変わる
@classmethod と @property を重ねて「クラスのプロパティ」を作る書き方は、版によって結果が違いました。
class P:
_n = 5
@classmethod
@property
def n(cls):
return cls._n
print("classmethod+property:", P.n)
| 版 | P.n の結果 |
|---|---|
| 3.10.18 | 5 |
| 3.11.15 | 5 |
| 3.14.7 | <bound method n of <class '__main__.P'>> |
公式ドキュメントの組み込み関数のページ(今回開いて確認)には、3.9でclassmethodがほかのデスクリプタ(property() など)を包めるようになり、3.11で非推奨、3.13で取り除かれたと書かれています。新しく書くコードでは、この重ね書きは使わず、ふつうのclassmethodとして P.n() と呼ぶ形にするのが、どの版でも同じ動きになる書き方です。
クラス本体の中でstaticmethodを呼ぶ
クラス定義の途中でstaticmethodをそのまま呼んでクラス変数を作る書き方は、今回試した3.10.18/3.11.15/3.14.7のすべてで動きました。
class Config:
@staticmethod
def plain():
return "static"
VALUE = plain()
print(Config.VALUE)
static
3.9以前は今回動かしていません(未実行)。
3種類の使い分けチェックリスト
| 確認すること | 目安 |
|---|---|
メソッドの中で self.〜 を使っているか |
使っていなければ、classmethodかstaticmethodにできる |
classmethodの中で cls(...) と書いているか |
クラス名を直接書くと、子クラスから呼んでも親ができる |
classmethod同士を cls. でつないでいるか |
子クラスの上書きが反映される |
クラス変数の更新が cls.x/クラス名.x になっているか |
self.x += 1 はインスタンス変数を作る |
staticmethodの引数に self や cls が残っていないか |
残っていると引数が1つずれる |
ファクトリメソッドの名前が from_〜 になっているか |
「何から作るか」が名前で伝わる |
| 戻り値の型ヒントが実装と合っているか | cls(...) なら Self、クラス名を直接書くなら "クラス名" |
よくある質問
Q. classmethodとstaticmethodの違いをひとことで言うと?
A. 第1引数にクラス(cls)が自動で渡されるのがclassmethod、何も渡されないのがstaticmethodです。そのため、classmethodはクラス変数の読み書きや cls(...) でのインスタンス生成ができ、継承したときは呼び出した子クラスが cls に入ります。staticmethodは引数だけで完結する処理に使います。
Q. インスタンスからclassmethodやstaticmethodを呼んでもよいですか?
A. 呼べます。obj.classmethod名() と呼んだ場合、cls にはそのインスタンスのクラスが渡ります(今回の実行では d.create("シロ") で cls = Dog)。ただし読む人には「インスタンスの値を使う処理」に見えやすいので、クラスから呼ぶ書き方にそろえると意図が伝わります。
Q. 「takes 0 positional arguments but 1 was given」と出ます。引数は渡していないのに、なぜ1個なのですか?
A. インスタンスから呼ぶと、Pythonが第1引数にインスタンスを自動で渡すためです。def の引数に self を書き忘れているか、@staticmethod を付け忘れているかのどちらかです。インスタンスの値を使うなら self を足し、使わないなら @staticmethod を付けます。
Q. staticmethodは使わずに、全部モジュール関数にしてもよいですか?
A. 動作のうえでは問題ありません。そのクラスだけで使う補助的な処理を名前でまとめたいとき、または子クラスで差し替えたいときにstaticmethodが役立ちます。ほかのクラスや処理からも広く使うなら、モジュール関数のほうが素直です。
まとめ
- 違いは「第1引数に何が自動で渡るか」 。インスタンスメソッドは
self、classmethodはcls、staticmethodは何も渡らない self.〜を使うならインスタンスメソッド、クラス自身を使うならclassmethod、どちらも使わないならstaticmethodかモジュール関数- classmethodは
from_〜形式のファクトリメソッドに最適。cls(...)で作れば、子クラスから呼ぶと子クラスのインスタンスが返る - 継承時、
clsは呼び出した子クラスを指す。cls.変数 += 1は子クラス側に変数を作る点に注意する but 1 was givenのように数が1つ多いエラーは、self/clsの書き忘れかデコレータの付け忘れのサイン- 戻り値の型ヒントは
typing.Self(3.11以降) 。3.10ではImportErrorになることを確認 - クラスに属する必然性が薄い処理はモジュール関数で十分。速さの差はナノ秒単位
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python classmethodとstaticmethodの違いと使い分け」
株式会社RJCでは、Pythonのクラス設計やメソッドの使い分けを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。