Classmethod とは
classmethodはPythonではクラスに属し、クラスのインスタンスではないメソッドを定義するために使用できるビルトインデコレータです。これは、クラスのインスタンスではなく、クラスそのものに対してclassmethodを呼び出すことができることを意味します。
classmethodの主な目的は、クラスが自身のクラスレベルの状態を変更またはアクセスできるようにすることです。これは、クラスの代替コンストラクタを作成するために便利であったり、クラスのインスタンスを返すファクトリメソッドを実装するために便利であったりします。
classmethodを定義するには、メソッドの定義の前に@classmethodデコレーターを追加する必要があります。メソッドの最初のパラメータはclsであり、クラス自体を参照します。以下は例です。
class MyClass:
    class_variable = 10
    @classmethod
    def class_method(cls, x):
        return cls.class_variable * x
result = MyClass.class_method(5)
print(result)  # Output: 50
この例では、class_variableはクラスレベルの変数であり、class_method()でアクセスおよび変更できます。 class_method()はパラメータxを取り、class_variableと掛け合わせた結果を返します。
まとめると、classmethodは、インスタンスではなくクラスに属するメソッドを定義することができるPythonの強力なツールであることがわかります。代替コンストラクタやファクトリメソッドの作成、クラスレベルの状態の変更やアクセスに一般的に使用されます。
Staticmethod とは
Pythonには、インスタンスやクラスレベルの状態にアクセスする必要がない場合に、クラスに属するメソッドを定義するための組み込みのstaticmethodデコレータが用意されています。staticmethodの使い方を示す例を紹介します。
人を表すPersonクラスがあるとし、2つの日付を受け取り、その差を年数で返すメソッドを定義したいとします。このメソッドは、インスタンスやクラスレベルの状態にアクセスする必要がないため、staticmethodの良い候補です。
import datetime
class Person:
    def __init__(self, name, birthdate):
        self.name = name
        self.birthdate = birthdate
    @staticmethod
    def calculate_age(birthdate, today):
        age = today.year - birthdate.year - ((today.month, today.day) < (birthdate.month, birthdate.day))
        return age
person1 = Person("John", datetime.date(1990, 1, 1))
today = datetime.date.today()
age = Person.calculate_age(person1.birthdate, today)
print(f"{person1.name} is {age} years old.")
この例では、calculate_ageメソッドをstaticmethodとして定義しています。このメソッドは、birthdateとtodayの2つの日付を受け取り、シンプルなアルゴリズムを使用してそれらの差を年数で計算します。
次に、名前と誕生日を持つPersonオブジェクトを作成し、datetime.date.today()メソッドを使って現在の日付を取得します。Personクラスのcalculate_ageメソッドを呼び出し、Personオブジェクトの誕生日と現在の日付を渡して、年齢を取得しています。最後に、人の名前と年齢を出力します。
staticmethodを使用することで、Personクラスに属するメソッドを定義することができましたが、インスタンスやクラスレベルの状態にアクセスする必要がないため、コードがより整理され、読みやすくなります。
Classmethod と Staticmethod の比較
classmethodとstaticmethodは最初には似ているように見えますが、実際には2つの間にはいくつかの重要な違いがあります。
classmethodは、クラス自体に対して、インスタンスではなくメソッドを操作する場合に使用されます。一般的に、clsと呼ばれるクラスを最初の引数として取ります。これにより、クラスレベルの状態にアクセスし、必要に応じて新しいクラスのインスタンスを作成することができます。
一方、 staticmethodは、クラスに属するが、特定のインスタンスに属するものではない場合に使用されます。これにはクラスを引数にとらず、クラスレベルの状態にアクセスすることはできません。これは、クラスに関連するユーティリティ関数によく使われますが、インスタンスレベルの状態にアクセスする必要がない場合に使用されます。
以下に、2つのメソッドの比較を示します。
- 
シグネチャ - classmethodはクラス自体を表す- clsパラメータを最初の引数として取ります。
- staticmethodは特別なパラメータを取りません。
 
- 
クラスレベルの状態にアクセスすることができるか - classmethodは- clsパラメータを介してクラスレベルの状態にアクセスできます。
- staticmethodはクラスレベルの状態にアクセスできません。
 
- 
新しいインスタンスを作成することができるか - classmethodは- clsパラメータを介してクラスの新しいインスタンスを作成できます。
- staticmethodはクラスの新しいインスタンスを作成することができません。
 
- 
継承 - classmethodはサブクラスに継承され、オーバーライドされる可能性があります。
- staticmethodもサブクラスに継承されますが、オーバーライドすることはできません。
 
以下は、違いを説明する例です。
class MyClass:
    class_var = "class variable"
    def __init__(self, inst_var):
        self.inst_var = inst_var
    @classmethod
    def from_class_method(cls, inst_var):
        return cls(inst_var + " (created by classmethod)")
    @staticmethod
    def from_static_method(inst_var):
        return MyClass(inst_var + " (created by staticmethod)")
instance = MyClass("instance variable")
instance2 = MyClass.from_class_method("instance variable")
instance3 = MyClass.from_static_method("instance variable")
print(instance.class_var)  # "class variable"
print(instance2.class_var)  # "class variable"
print(instance3.class_var)  # "class variable"
print(instance.inst_var)  # "instance variable"
print(instance2.inst_var)  # "instance variable (created by classmethod)"
print(instance3.inst_var)  # "instance variable (created by staticmethod)"
この例では、class_varとinst_varを持つMyClassを定義しています。そして、クラスを生成するためのclassmethodとstaticmethodを定義しています。
from_class_methodメソッドを呼び出すと、指定されたinst_varを持つ新しいクラスのインスタンスを作成して、それを返します。from_static_methodメソッドを呼び出すと、新しいクラスのインスタンスを作成して、それを返します。
出力からわかるように、両方のメソッドはクラス変数にアクセスできますが、classmethodのみがクラスのインスタンスをアクセスして生成することができます。
参考