はじめに
私自身、実務で @classmethod
をよく使っているにも関わらず「何のためにあるの?」と聞かれたときにうまく答えられなかった経験から、
基本から整理し直してみた内容をこの記事でまとめてみます。
-
self
とcls
の違いって何? -
@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
なぜ self
や cls
を書かないといけないのか?
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
self
や cls
を省略すると「自動で渡された引数を受け取る場所がない」のでエラーになります。
✅ 自動的に渡される引数の一覧
メソッドの種類 | 第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などと組み合わせて使うときには、その本質がよく活きてきます。
これまでself
や cls
の違いをなんとなくで理解していたので、これを機に理解がグッと深まったと思います。