Pythonでクラスを書くときに知っておきたい `self` と `@classmethod` の違いと使い分け


## はじめに

私自身、実務で `@classmethod` をよく使っているにも関わらず「何のためにあるの？」と聞かれたときにうまく答えられなかった経験から、  
基本から整理し直してみた内容をこの記事でまとめてみます。

- `self` と `cls` の違いって何？
- `@classmethod` っていつ使うべき？


---

## クラスとは？ 〜Pythonクラスの基本おさらい〜

Pythonのクラスは「**データとその操作をまとめた設計図**」です。

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

    def greet(self):
        print(f"こんにちは、{self.name}さん！")
```

```python
u = User("Alice")
u.greet()  # → こんにちは、Aliceさん！
```

- `__init__` は「インスタンスを作るとき」に呼ばれる特別なメソッド
- `self` は「このインスタンス自身」を指します

---

## `self` とは？

`self` はインスタンスメソッドの第1引数として使われ、  
**インスタンスごとのデータや振る舞いを操作するため**に使います。

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

    def rename(self, new_name):
        self.name = new_name
```

```python
user = User("Taro")
user.rename("Jiro")
print(user.name)  # → Jiro
```

---

## `@classmethod` とは？

`@classmethod` を付けると、**クラスに対して動作するメソッド**になります。  
第1引数には `cls` を取り、**クラス自体（Userクラスなど）を受け取る**のが特徴です。

```python
class User:
    @classmethod
    def hello(cls):
        print(f"Hello from {cls.__name__}")
```

```python
User.hello()  # → Hello from User
```

---

## なぜ `self` や `cls` を書かないといけないのか？

Pythonでは、インスタンスメソッドやクラスメソッドを呼ぶときに  
**自動的に第1引数が渡される**という仕組みになっています。

```python
class User:
    def greet(self):
        print("こんにちは！")

u = User()
u.greet()  # 裏では → User.greet(u)
```

同様に、クラスメソッドでは：

```python
class User:
    @classmethod
    def hello(cls):
        print(f"Hello from {cls.__name__}")

User.hello()  # 裏では → User.hello(User)
```

---

### self / cls を書かないとどうなる？

```python
class Bad:
    def broken():
        print("壊れた")

b = Bad()
b.broken()  # TypeError: broken() takes 0 positional arguments but 1 was given
```

`self` や `cls` を省略すると「自動で渡された引数を受け取る場所がない」のでエラーになります。

---

### ✅ 自動的に渡される引数の一覧

| メソッドの種類       | 第1引数 | 自動で渡される値         |
|--------------------|----------|------------------------|
| インスタンスメソッド | `self`   | 呼び出したインスタンス |
| クラスメソッド       | `cls`    | 呼び出したクラス       |
| スタティックメソッド | なし     | なし（自動引数なし）    |


---

## `@classmethod` はいつ使うの？

### ✅ インスタンスがまだ存在しないとき
selfが必要な通常のインスタンスメソッドだと，クラスをインスタンス化した後でないとそのメソッドを呼び出せませんが，`@classmethod`の場合は，インスタンス化されてなくても呼び出すことができます．

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

    @classmethod
    def from_email(cls, email):
        name = email.split('@')[0].capitalize()
        return cls(name)
```

```python
user = User.from_email("yamada@example.com")
print(user.name)  # → Yamada
```

「インスタンスがない状態から何かを生成する処理」＝ `@classmethod` 向きです。

---

### ✅ クラス全体に関わる操作をしたいとき

```python
class Product:
    tax_rate = 0.1

    @classmethod
    def set_tax_rate(cls, rate):
        cls.tax_rate = rate
```

```python
Product.set_tax_rate(0.2)
print(Product.tax_rate)  # → 0.2
```
クラスに共通する値などを操作しています．

---

クラスをインスタンス化する前からメソッドを呼び出す具体例です。  
特に実務などでもよくある場面ですが、**idからユーザーを検索するメソッドなどはインスタンス化する前に呼び出せる必要があります。**

ここで `cls` を使うことで、「どのクラスから呼ばれているか」に応じて柔軟に挙動を変えることもできます。

```python
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String)

    @classmethod
    def get_by_id(cls, session, user_id):
        return session.query(cls).filter_by(id=user_id).first()

    @classmethod
    def update_name(cls, session, user_id, new_name):
        user = cls.get_by_id(session, user_id)
        if user:
            user.name = new_name
            session.commit()
```

```python
User.update_name(session, 1, "Hanako")
```

- `self` ではなく `cls` を使うことで **インスタンスがなくても呼べる**
- DBとのやり取り（検索・生成など）において「クラス全体に関わる操作」が可能になる
- 継承されたクラスから呼び出した場合でも、適切にそのクラスを使うことができる
---

## self / cls / staticmethod の違いまとめ

| タイプ | デコレータ | 第1引数 | 呼び出し元 | 用途 |
|--------|------------|---------|------------|------|
| インスタンスメソッド | なし | `self` | インスタンス | 特定のデータ操作 |
| クラスメソッド | `@classmethod` | `cls` | クラス or インスタンス | クラス全体の操作・生成 |
| スタティックメソッド | `@staticmethod` | なし | クラス or インスタンス | 状態に依存しない処理 |

---

## どんなときに `@classmethod` を使うべき？

| 目的・状況 | self or cls? | 理由 |
|-------------|----------------|-------|
| 自分の名前を変更したい | `self` | そのインスタンスにしか関係ない |
| IDからDB検索したい | `cls` | インスタンスがまだ存在しない |
| 全体の設定値を変更したい | `cls` | クラスに属する情報だから |
| 引数だけで動くユーティリティ関数 | `@staticmethod` | self / cls 不要 |

---

## おわりに

`@classmethod` は「インスタンスを介さずにクラスに属する操作ができる」非常に便利な機能です。  
特にORMなどと組み合わせて使うときには、その本質がよく活きてきます。

これまで`self` や `cls` の違いをなんとなくで理解していたので、これを機に理解がグッと深まったと思います。