1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

はじめに

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

  • selfcls の違いって何?
  • @classmethod っていつ使うべき?

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

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

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

    def greet(self):
        print(f"こんにちは、{self.name}さん!")
u = User("Alice")
u.greet()  # → こんにちは、Aliceさん!
  • __init__ は「インスタンスを作るとき」に呼ばれる特別なメソッド
  • self は「このインスタンス自身」を指します

self とは?

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

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

    def rename(self, new_name):
        self.name = new_name
user = User("Taro")
user.rename("Jiro")
print(user.name)  # → Jiro

@classmethod とは?

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

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

なぜ selfcls を書かないといけないのか?

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

class User:
    def greet(self):
        print("こんにちは!")

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

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

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

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

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

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

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

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


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

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

@classmethod はいつ使うの?

✅ インスタンスがまだ存在しないとき

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

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

    @classmethod
    def from_email(cls, email):
        name = email.split('@')[0].capitalize()
        return cls(name)
user = User.from_email("yamada@example.com")
print(user.name)  # → Yamada

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


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

class Product:
    tax_rate = 0.1

    @classmethod
    def set_tax_rate(cls, rate):
        cls.tax_rate = rate
Product.set_tax_rate(0.2)
print(Product.tax_rate)  # → 0.2

クラスに共通する値などを操作しています.


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

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

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()
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などと組み合わせて使うときには、その本質がよく活きてきます。

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

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?