---
title: "Pythonのclsとは？@classmethodの使い方とself・staticmethodとの違い【3.15対応】"
url: "https://www.issoh.co.jp/tech/details/3826/"
published: 2024-10-11
updated: 2026-11-02
categories: ["Python"]
publisher: "株式会社一創"
---

# Pythonのclsとは？@classmethodの使い方とself・staticmethodとの違い【3.15対応】

Pythonのコードに出てくる`cls`は、`@classmethod`を付けたメソッドの第一引数に自動で渡される「クラス自身」です。インスタンスを受け取る`self`と位置は同じで、中身がクラスである点だけが違います。この記事では、clsとselfの違い、staticmethodとの使い分け、typing.Selfの型ヒント、クラス変数の落とし穴、Python 3.13で削除された書き方までを、3.10〜3.15.0で実行した結果で説明します。

## まとめ：clsと@classmethodの要点

- `cls`は`@classmethod`の第一引数に入るクラスオブジェクトです。名前は[PEP 8](https://peps.python.org/pep-0008/#function-and-method-arguments)の規約で、言語の予約語ではありません。
- 主な用途は`from_dict()`のような代替コンストラクタです。`return cls(...)`と書けば、サブクラスから呼んだときにサブクラスのインスタンスが返ります。
- 選び方はメソッド本体が何に触るかで決まります。selfに触るならインスタンスメソッド、clsに触るならクラスメソッドで、どちらにも触らない処理はスタティックメソッドより先にモジュール関数を検討します。
- 代替コンストラクタの戻り値は`-> Self`（Python 3.11以上）で注釈します。`-> "Base"`と書くと、サブクラスのメソッドを呼んだ行でmypyがエラーを出します。
- `cls.count += 1`はサブクラスから呼ぶとサブクラス側に別の変数を作ります。全体で共有する値はクラス名で更新します。
- `@classmethod`と`@property`の重ね掛けは3.13で削除されました。3.12までは警告なしで動くので、移行前にコードを検索して書き換えます。

## Pythonのclsとは：@classmethodの第一引数に入るクラス

`def`の直前に`@classmethod`を置くと、そのメソッドはクラスから直接呼べるようになり、第一引数には呼び出したクラスが入ります。[Python公式ドキュメントの組み込み関数classmethod](https://docs.python.org/ja/3/library/functions.html#classmethod)は「クラスメソッドは、インスタンスメソッドが暗黙の第一引数としてインスタンスをとるように、第一引数としてクラスをとります。」と説明しています。

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

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


t = Temperature.from_fahrenheit(212)
print(t.celsius)
# 100.0
```

### clsとselfの違い

違いは第一引数に何が入るかだけです。`self`には`t.method()`のドットの左にあるインスタンスが、`cls`には`Temperature.from_fahrenheit()`のドットの左にあるクラスが入ります。そのため`cls`からはクラス変数には届きますが、`self.celsius`のようなインスタンス変数には届きません。

PEP 8は「Always use cls for the first argument to class methods.」（クラスメソッドの第一引数には常にclsを使う）と定め、インスタンスメソッドのselfにも同じ規約を置いています。

### @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
```

`@staticmethod`や`@property`、フレームワークのデコレータも同じ仕組みです。複数重ねると下から順に適用されるため、後述するPydanticのように並べる順序が結果を変えることがあります。

### cls()とreturn clsの意味

`cls`はクラスそのものなので、`cls(...)`は`Temperature(...)`と書くのと同じくインスタンスを作ります。クラスメソッドの中で`return cls(...)`と書くのは「呼び出したクラスのインスタンスを返す」という意味です。クラス名を直接書かずに`cls`を使う理由は、継承したときに効いてきます（後述の「継承したときにclsが指すクラス」）。一方、`return cls`と括弧なしで書くと、インスタンスではなくクラスオブジェクトそのものが返ります。チェーンの途中でクラスを返したい特殊な場合以外は、括弧の付け忘れを疑ってください。

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

Pythonのクラスに書くメソッドは3種類で、違いは第一引数に何が自動で渡るかです。

```
class Order:
    tax_rate = 0.1

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

    def total(self):
        # インスタンスメソッド: self.amount を使う
        return self.amount * (1 + self.tax_rate)

    @classmethod
    def set_tax_rate(cls, rate):
        # クラスメソッド: クラス変数を書き換える
        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
```

### 第一引数と呼び出し方の比較表

| 種類         | デコレータ         | 第一引数         | 触れる対象            | インスタンスなしで呼ぶ |
| ---------- | ------------- | ------------ | ---------------- | ----------- |
| インスタンスメソッド | なし            | self（インスタンス） | インスタンス変数・クラス変数   | 不可          |
| クラスメソッド    | @classmethod  | cls（クラス）     | クラス変数・cls()による生成 | 可           |
| スタティックメソッド | @staticmethod | 自動では渡らない     | 引数で渡した値          | 可           |

公式ドキュメントは「クラスメソッドは C++ や Java の静的メソッドとは異なります。静的メソッドは、この節の staticmethod() を参照してください。」と注意しています。他言語の静的メソッドに当たるのは`@staticmethod`のほうで、`@classmethod`はクラスを受け取る分だけできることが多い、と覚えると混同しません。

### どれを選ぶかの判断順序

メソッドの中で何を使うかを、上から順に確かめれば決まります。

1. `self.amount`のようにインスタンスの値を使う → インスタンスメソッド
2. インスタンスは使わないが、クラス変数か`cls(...)`による生成を使う → クラスメソッド
3. どちらも使わず引数だけで完結する → スタティックメソッド、またはモジュール関数

3番目は、まずモジュール関数で足りないかを考えます。クラスに置く価値があるのは、`Order.is_valid_amount`のように「どのクラスの道具か」が名前から伝わる場合と、サブクラスで差し替えたい場合です。迷ったらインスタンスメソッドを既定にしてください。後からインスタンスの値が必要になったとき、クラスメソッドからの書き換えは呼び出し側まで波及します。

## 代替コンストラクタをfrom\_で始まるクラスメソッドで書く

クラスメソッドの用途の中心は代替コンストラクタです。公式ドキュメントは`staticmethod`の項で「別のコンストラクタをクラスに用意する際に便利な @classmethod も参照してください」と案内しています。`__init__`は1クラスに1つしか書けませんが、辞書から、CSVの1行から、APIの応答から、と生成経路は増えていきます。この分岐を`__init__`の引数と型判定で吸収すると引数が膨らむので、経路ごとにクラスメソッドを用意します。

### 辞書やCSVの行からインスタンスを作る

```
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
```

標準ライブラリも同じ発想の命名です。`dict.fromkeys()`、`int.from_bytes()`、`datetime.fromisoformat()`はどれも「何から作るか」を名前に含めています。自作の代替コンストラクタも`from_`で始めておけば、読み手は中身を見る前に用途を判断できます。生成するクラス自体を入力によって切り替えたい場合はファクトリーメソッドの領域で、[PythonのFactory Method（ファクトリメソッド）実装｜ファクトリ関数・classmethodとの使い分け](/tech/details/8639/)で書き分けを扱っています。

### 戻り値の型ヒントはtyping.Selfで書く

代替コンストラクタの戻り値は、Python 3.11で追加された`typing.Self`で注釈します。[公式ドキュメントのtyping.Self](https://docs.python.org/ja/3/library/typing.html#typing.Self)は、selfを返すメソッドに続く「他によくある用途」として「代替コンストラクタとして使われ、cls 引数のインスタンスを返す classmethod」を挙げています。クラス名を文字列で書く`-> "Base"`との違いは、型チェッカーに通すと分かります。

```
from typing import Self


class Base:
    def __init__(self, value: int) -> None:
        self.value = value

    @classmethod
    def from_str(cls, text: str) -> Self:
        return cls(int(text))

    @classmethod
    def from_str_base(cls, text: str) -> "Base":
        return cls(int(text))


class Child(Base):
    def double(self) -> int:
        return self.value * 2


c = Child.from_str("21")
print(type(c).__name__, c.double())
# Child 42
d = Child.from_str_base("21")
print(type(d).__name__, d.double())
# Child 42
```

実行するとどちらも`Child 42`を出します。ところが末尾に`reveal_type(c)`と`reveal_type(d)`を足してmypy 2.4.1で検査すると、`reveal_type(c)`は`Child`、`reveal_type(d)`は`Base`と推論され、最後の`d.double()`の行に`error: "Base" has no attribute "double"`が出ました。実行時は正しく動くのに、注釈のせいで型検査だけが落ちる状態です。`cls(...)`で生成しているなら`Self`、メソッド内でクラス名を直接書いて常に親を返すなら`"Base"`、と実装に合わせてください。公式ドキュメントも、サブクラスのインスタンスを返す保証がないメソッドに`Self`を使うべきではないと書いています。

Python 3.10で`from typing import Self`を実行すると`ImportError: cannot import name 'Self' from 'typing'`で止まります。3.10以前も対象にするなら、外部パッケージ`typing_extensions`から読み込みます。

## 継承したときにclsが指すクラス

公式ドキュメントは「クラスメソッドが派生クラスから呼び出される場合は、その派生クラスオブジェクトが暗黙の第一引数として渡されます」と定めています。親クラスで定義したメソッドでも、`cls`に入るのは呼び出したサブクラスです。

### return cls()とreturn Base()の差

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

    @staticmethod
    def create_base():
        return Base()


class Sub(Base):
    pass


print(type(Sub.create()).__name__)
# Sub
print(type(Sub.create_base()).__name__)
# Base
```

`Sub.create()`はBaseに書いたメソッドを実行しながら、Subのインスタンスを返します。クラス名を直接書いた`create_base()`は、サブクラスから呼んでも常にBaseを返します。スタティックメソッドは自分がどのクラスとして呼ばれたかを知る手段を持たないので、継承先で結果を変えたい生成処理には使えません。「サブクラスごとに結果が変わるべきか」が、classmethodとstaticmethodを分ける実務上の判断軸です。

### super()で親のクラスメソッドを再利用する

サブクラスで代替コンストラクタを上書きするときは、`super().from_dict(data)`で親の処理を呼べます。このとき親のメソッドに渡る`cls`はサブクラスのままなので、親の`cls(...)`はサブクラスのインスタンスを作ります。

```
class Shape:
    def __init__(self, name):
        self.name = name

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


class Circle(Shape):
    def __init__(self, name, radius=1.0):
        super().__init__(name)
        self.radius = radius

    @classmethod
    def from_dict(cls, data):
        obj = super().from_dict(data)  # cls=Circle のまま親の処理を使う
        obj.radius = data.get("radius", 1.0)
        return obj


c = Circle.from_dict({"name": "c1", "radius": 2.5})
print(type(c).__name__, c.name, c.radius)
# Circle c1 2.5
```

### \_\_init\_subclass\_\_と抽象クラスメソッド

サブクラスが定義された時点で呼ばれる`__init_subclass__`は、デコレータを書かなくてもクラスメソッドとして扱われます。[データモデルの説明](https://docs.python.org/ja/3/reference/datamodel.html#object.%5F%5Finit%5Fsubclass%5F%5F)には「もし、このメソッドがインスタンスメソッドとして定義されると、暗黙的にクラスメソッドに変換されます」とあり、メタクラスなしでサブクラスの登録処理を書けます。

```
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](https://docs.python.org/ja/3/library/abc.html#abc.abstractclassmethod)はPython 3.3で非推奨になっており、新しく書くコードで使う理由はありません。

## クラス変数をクラスメソッドで更新するときの落とし穴

クラス変数の更新口をクラスメソッドにまとめるのはよくある設計ですが、継承が絡むと`cls`の性質が裏目に出ます。

### cls.count += 1はサブクラス側に別の変数を作る

```
class Session:
    active = 0

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


class AdminSession(Session):
    pass


Session.open()
AdminSession.open()
AdminSession.open()
print(Session.active, AdminSession.active, "active" in AdminSession.__dict__)
# 1 3 True
```

3回開いたのに`Session.active`は1のままです。`AdminSession.open()`では`cls`がAdminSessionなので、`cls.active += 1`は「親の値1を読み、AdminSession自身に2を書く」動作になり、以後AdminSessionは自分専用の`active`を持ちます。Python 3.10・3.15.0のどちらでも同じ結果でした。全サブクラスで共有するカウンタなら`Session.active += 1`とクラス名で書き、インスタンス生成だけを`cls()`に任せます。逆に、サブクラスごとに別々に数えたいなら`cls.active`の動作は意図どおりです。どちらを望むのかを決めてから書いてください。

### インスタンス経由の代入と可変オブジェクトのクラス変数

インスタンスから`s.active = 99`と代入すると、クラス変数は変わらず、そのインスタンスにだけ同名のインスタンス変数が作られます。更新はクラスメソッドかクラス名経由に限定します。また、クラス変数に置いたリストや辞書は全インスタンスとサブクラスで共有されるので、`Plugin.registry`のように意図して共有する場合以外は、`__init__`の中で`self.items = []`と初期化してください。

## @classmethodを付け忘れたときのTypeError

デコレータの付け忘れは、`missing`の後ろに出る引数名がどれかで見分けられます。

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

    def from_fahrenheit(cls, fahrenheit):  # @classmethod を付け忘れた
        return cls(round((fahrenheit - 32) / 1.8, 1))

    def describe(self):
        return f"{self.celsius}℃"

    @classmethod
    def describe_all(cls):
        return cls.describe()  # インスタンスメソッドを cls から呼んだ


for f in (lambda: Temperature.from_fahrenheit(212), lambda: Temperature.describe_all()):
    try:
        f()
    except TypeError as e:
        print("TypeError:", e)
# TypeError: Temperature.from_fahrenheit() missing 1 required positional argument: 'fahrenheit'
# TypeError: Temperature.describe() missing 1 required positional argument: 'self'
```

1つめは、デコレータがないので`212`が`cls`の位置に入り、`fahrenheit`が足りなくなっています。「渡した覚えのある引数が足りないと言われる」ときは、デコレータの付け忘れを疑います。2つめは、`cls.describe()`がクラスからインスタンスメソッドを呼ぶのと同じなので`self`に入るものがありません。インスタンスの値が要る処理は、クラスメソッドからは呼べないということです。メッセージはPython 3.10と3.15.0で同じ文面でした。

## classmethodの仕組み：デスクリプタの\_\_get\_\_がclsを束縛する

`cls`が自動で入る仕組みは、デスクリプタ（属性アクセスに割り込むオブジェクト）で説明できます。[公式のデスクリプタ HowTo ガイド](https://docs.python.org/ja/3/howto/descriptor.html#class-methods)には、`classmethod`と同じ動きをする純粋Pythonの実装が載っています。これを動かすと、組み込みと同じ結果になります。

```
import functools
from types import MethodType


class ClassMethod:
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f
        functools.update_wrapper(self, f)

    def __get__(self, obj, cls=None):
        if cls is None:
            cls = type(obj)
        return MethodType(self.f, cls)


class A:
    @ClassMethod
    def who(cls):
        return cls.__name__


class B(A):
    pass


print(A.who(), B.who(), B().who())
# A B B
print(A.__dict__["who"].__get__(None, B))
# <bound method A.who of <class '__main__.B'>>
```

`B.who`と書いた瞬間に`__get__(None, B)`が呼ばれ、関数の第一引数をBで埋めた束縛メソッドが返ります。インスタンス`B()`から呼んだときも、属性検索の仕組みが`__get__(b, type(b))`の形でクラスを渡すので、`cls`にはBが入ります（`if cls is None`の分岐は`__get__(obj)`を直接呼ばれたときの保険です）。「インスタンスから呼んでもclsにはクラスが入る」「サブクラスから呼ぶとサブクラスが入る」という前の章の挙動は、この数行と属性検索の仕組みから出てきます。デスクリプタを含むPythonのメタプログラミング全般は扱わず、ここでは`cls`の正体を確かめるところまでにとどめます。

## 標準ライブラリとPydanticでのクラスメソッド

標準ライブラリの代替コンストラクタが実際にクラスメソッドかどうかは、`inspect.getattr_static()`でデスクリプタを取り出すと確認できます。

```
import datetime
import inspect
import pathlib

for owner, name in [(dict, "fromkeys"), (int, "from_bytes"),
                    (datetime.datetime, "fromisoformat"), (pathlib.Path, "cwd")]:
    print(f"{owner.__name__}.{name}:", type(inspect.getattr_static(owner, name)).__name__)
# dict.fromkeys: classmethod_descriptor
# int.from_bytes: classmethod_descriptor
# datetime.fromisoformat: classmethod_descriptor
# Path.cwd: classmethod
```

C実装の型は`classmethod_descriptor`、Pythonで書かれた`pathlib`は`classmethod`と表示が違うだけで、どちらもクラスメソッドです。`Path.cwd()`の使い方は[Path(\_\_file\_\_).resolve().parentとは｜Python pathlibのresolve()・absolute()の違いとBASE\_DIRの書き方【3.15対応】](/tech/details/5986/)で扱っています。

フレームワークでは、[Pydanticとは？読み方・BaseModelの使い方とV1との違い【2.13対応】](/tech/details/4838/)で扱うPydantic v2の`field_validator`（およびbefore・wrapの`model_validator`）がクラスメソッドで書かれます。[Pydanticのバリデータのドキュメント](https://docs.pydantic.dev/latest/concepts/validators/)の例はすべて`@field_validator`を上、`@classmethod`を下に重ねています。値の検証はインスタンスの生成途中で走るので、まだ完成していないインスタンスの`self`ではなくクラスを受け取ります。

```
from pydantic import BaseModel, field_validator


class Good(BaseModel):
    name: str

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


class Reversed(BaseModel):
    name: str

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


print(repr(Good(name="  taro ").name), repr(Reversed(name="  taro ").name))
# 'taro' '  taro '
```

pydantic 2.14.2で、順序を逆にした`Reversed`は例外も警告も出さずに検証を素通りさせ、前後の空白が残りました。`-W always`で警告を全表示にしても何も出ません。Pydantic自身は順序の誤りを知らせないので、後述の重ね掛け検出スクリプトと同じ要領で`@classmethod`が`@field_validator`より上にある関数を探すか、プロジェクトのテンプレートに正しい順序を固定しておきます。

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

組み込み関数の説明に載っている変更は、3.9のデスクリプタのラップ対応、3.10の属性継承、3.11の非推奨と3.13の削除の3つです。Python 3.14と[Python 3.15の新機能｜遅延インポートとUTF-8既定化、3.14から上げる手順](/tech/details/18312/)で扱う3.15では、classmethod自体の変更はありません。

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

括弧なしで`Config.name`と読めるクラスプロパティを作る目的で、次の書き方が使われてきました。

```
import sys


class Config:
    _name = "prod"

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


print(sys.version.split()[0], repr(Config.name))
```

Python 3.9で「クラスメソッドは property() など、他のデスクリプタをラップすることができるようになりました」と変更されたことで動くようになり、3.11で非推奨、3.13で削除されました。[3.13の変更点](https://docs.python.org/ja/3/whatsnew/3.13.html#builtins)は削除の理由を「この機能には基本的な設計上の欠陥があり、いくつもの問題を引き起こしていました」と説明しています。各版で実行した結果は次のとおりです。

| 版       | Config.nameの値 | 警告 |
| ------- | ------------- | -- |
| 3.9.6   | ‘prod’        | なし |
| 3.10.20 | ‘prod’        | なし |
| 3.11.15 | ‘prod’        | なし |
| 3.12.13 | ‘prod’        | なし |
| 3.13.14 | 束縛メソッド        | なし |
| 3.14.6  | 束縛メソッド        | なし |
| 3.15.0  | 束縛メソッド        | なし |

2026年11月時点の最新は3.15.2・3.14.10ですが、上の表はそれより前のパッチ版で実行した結果です。

非推奨とされた3.11と3.12でも`DeprecationWarning`は出ず、正しく動きます。3.13以降は例外にもならず、`Config.name`が`<bound method name of <class '__main__.Config'>>`を返します。f文字列やログに混ぜている箇所はそのまま通り、`Config.name.upper()`のように文字列として扱った行で初めて`AttributeError`になります。壊れた場所と落ちる場所が離れるので、[Python 3.13の新機能と3.12との違い｜3.13.17の変更点・サポート期限・GIL無効化の実測](/tech/details/4042/)への移行前に、コード側から探しておく必要があります。書き換えは、括弧付きで呼ぶ普通のクラスメソッドに戻すのが最も単純です。

```
class Config:
    _name = "prod"

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


print(Config.name())
# prod
```

### 重ね掛けをコードベースから探すスクリプト

警告が出ない以上、実行時のログからは移行対象を拾えません。標準の`ast`モジュールでデコレータの並びを調べれば、`@classmethod`の直下に`@property`がある関数を一覧にできます。`grep`と違い、間に空行やコメントがあっても、`@builtins.classmethod`と書かれていても拾えます。

```
import ast
import pathlib
import sys


def name_of(dec):
    # @classmethod と @builtins.classmethod を同じ名前に直す
    if isinstance(dec, ast.Name):
        return dec.id
    if isinstance(dec, ast.Attribute):
        return dec.attr
    return None


root = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else ".")
for path in root.rglob("*.py"):
    try:
        tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
    except (SyntaxError, UnicodeDecodeError) as e:
        print(f"skip {path}: {e}", file=sys.stderr)
        continue
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
            names = [name_of(d) for d in node.decorator_list]
            for upper, lower in zip(names, names[1:]):
                if upper == "classmethod" and lower == "property":
                    print(f"{path}:{node.lineno}: {node.name}")
```

`python find_classproperty.py src`のように対象ディレクトリを渡すと、`src/config.py:9: name`の形で`def`の行番号を出します（3.9.6・3.10.20・3.15.0で同じ結果）。CIに組み込めば新たな混入も止められます。構文エラーのファイルは`skip`と出して飛ばします。`@functools.cached_property`など他のデスクリプタとの重ね掛けも3.13で使えなくなるので、それらも探すなら`lower == "property"`の条件を広げてください。

### Python 3.10で変わったメソッド属性とstaticmethodの呼び出し

3.10から、クラスメソッドとスタティックメソッドは元の関数の`__module__`・`__name__`・`__qualname__`・`__doc__`・`__annotations__`を引き継ぎ、`__wrapped__`属性を持つようになりました。3.9以前は、クラスの`__dict__`から取り出したclassmethodオブジェクト自体の`__doc__`がclassmethod型の説明文になり、`__wrapped__`もありません。同じ3.10で、スタティックメソッドはクラス本体の中から`f()`と普通の関数のように呼べるようになりました。

## よくある質問

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

呼び出せます。公式ドキュメントも、クラスから（`C.f()`）でもインスタンスから（`C().f()`）でも呼べ、インスタンスはそのクラス以外無視されると明記しています。ただし`t.from_fahrenheit()`のように書くと、読み手は`t`の値を使う処理だと誤読します。`Temperature.from_fahrenheit()`とクラス名で呼べば、インスタンスに依存しないことが呼び出し行だけで分かります。

### 3.13以降で括弧なしのクラスプロパティを作るには？

メタクラスに`@property`を定義します。`class ConfigMeta(type):`に`name`プロパティを書き、`class Config(metaclass=ConfigMeta):`とすると、`Config.name`もサブクラスの`StagingConfig.name`も括弧なしで読めました（3.10・3.15.0で確認）。ただしインスタンスからは読めず、メタクラスは他のメタクラスを使うクラスと衝突しやすいので、普通は`Config.name()`で足ります。

### クラスメソッドから別のクラスメソッドやスタティックメソッドを呼ぶには？

`cls.helper()`のように`cls`経由で呼びます。クラス名で`Order.helper()`と書くと、サブクラスでhelperを上書きしても親の実装が呼ばれます。メソッドの中から`helper()`と名前だけで呼ぶと`NameError`になります。

### クラスメソッドの呼び出しは遅いですか？

`timeit.repeat(number=2_000_000, repeat=5)`の最小値を1回あたりに換算すると、Python 3.15.0（Intel Core i9-9880H）でモジュール関数約35ns、インスタンスメソッド約40ns、スタティックメソッド約65ns、クラスメソッド約85nsでした（計測のたびに数ns揺れます）。3.14.6でも順位は同じで、クラスメソッドはインスタンスメソッドの約2倍です。差は数十ナノ秒なので、1秒間に数百万回呼ぶ内側のループでない限り、設計の意図で選んで構いません。

### clsという名前は別の名前に変えても動きますか？

動きます。第一引数の名前は言語仕様では決まっておらず、`klass`や`c`でもクラスが渡されます。ただ、PEP 8が`cls`を使うよう定めており、リンターや読み手もこの名前を前提にするため、変える利点はありません。`class`は予約語なので第一引数の名前には使えません。

クラス設計の段階から業務システムをPythonで作りたい場合は、[フルスクラッチ開発](/service/system/fullscratch/)でご相談いただけます。

## 関連記事

- [PythonのFactory Method（ファクトリメソッド）実装｜ファクトリ関数・classmethodとの使い分け](/tech/details/8639/)
- [Pydanticとは？読み方・BaseModelの使い方とV1との違い【2.13対応】](/tech/details/4838/)
- [Path(\_\_file\_\_).resolve().parentとは｜Python pathlibのresolve()・absolute()の違いとBASE\_DIRの書き方【3.15対応】](/tech/details/5986/)
- [Python 3.13の新機能と3.12との違い｜3.13.17の変更点・サポート期限・GIL無効化の実測](/tech/details/4042/)
- [Python 3.15の新機能｜遅延インポートとUTF-8既定化、3.14から上げる手順](/tech/details/18312/)

---

出典: [Pythonのclsとは？@classmethodの使い方とself・staticmethodとの違い【3.15対応】](<https://www.issoh.co.jp/tech/details/3826/>)（株式会社一創）
