Pythonのクラスメソッド(class method)の定義の仕方とstaticmethodとの違い

こんにちは。今日は上の件について書きます。

○クラスメソッド (class method) とは

クラスメソッドとは、クラス内で定義されたメソッドで、インスタンス化しなくても呼び出すことができるメソッドのことです。
これは、インスタンスではなくて、クラスそのものに対してなんらかの操作をするメソッドを定義するときに用います。
普通、クラス内のメソッドを呼び出そうとした場合は、一度インスタンス化しないといけません。例えば、以下のクラスを定義したと思います。
# 普通のクラス
class SampleClass:
    
    def sample_method(self):
        print("sample")
これのクラスのメソッドを呼び出そうとしたとき、以下のようにするとエラーがでます。
# error!!
SampleClass.sample_method()
クラス内のメソッドを使おうとした場合は、まずインスタンス化して、その後、そのインスタンスからメソッドを呼び出す必要があります。
# 一度インスタンス化
sample_class = SampleClass()

# methodを呼び出す
sample_class.sample_method()

# 出力がちゃんとでる

ここで、クラスメソッドを使うと、インスタンス化することなく、メソッドを呼び出すことができます。
# クラスメソッドを使った例
class SampleClassMethod:

    @classmethod
    def samle_method(cls):
        print("sample")

# インスタンス化することなく、メソッドを呼び出せる
SampleClassMethod.samle_method()

# 出力は"sample"
こんな風な呼び出しを可能にするのが、クラスメソッドです。

○クラスメソッドの定義と呼び出し

クラスメソッドの定義の方法には次の二つの方法があります。
  • classmethod()関数を使う
  • @classmethodというデコレーターを使う
それぞれのやり方を見ていきます。
■classmethod()関数を使う場合
これはあんまり推奨されていないやり方っぽいので、紹介だけします。
class SampleClassMethod:
    def sample_method(cls):
        return clf
    sample_method = classmethod(sample_method)
クラス内で、クラスメソッド化したいメソッドを再度定義することで実現できます。これは、以下で紹介するデコレーターと基本的には同じ動作をするので、以下の方を使えばいいかと思います。
■@classmethodのデコレーターを使う
以下のような感じです。
class SampleClassMethod:
    @classmethod
    def sample_method(cls):
        return clf
こっちの方がPythonっぽいといえばそうかもしれないです笑。

○staticmethodとclassmethodの違い

似てないんですけど、混乱しがちなものにstaticmethodがあります。どちらもインスタンスを作成せずともメソッドを呼び出すことができるのですが、違いがあります。
簡単に特徴を述べると以下のような違いがあります。
  • staticmethod : こちらは、クラス内に定義されていても、クラスに関係なく動き、受け取った引数のみ考慮します。乱暴に言うと、ただの関数と変わりません。
  • classmethod : こちらは、クラス自身を引数として受け取り、受け取った引数と共に用いることができます。
具体的な例をみながら、classmethodの特徴と、staticmethodとの違いについてみたいと思います。まず、以下のようなクラスを用意します。
class DiffClassStaticMethod:
    @classmethod
    def class_meth(*args):
        return args
    
    @staticmethod
    def static_meth(*args):
        return args
まずclassmethodを使ってみます。
# classmethodの方の呼び出し
# 引数に何も用意しなくても、Demo.klassmethはDemoクラスを第一引数として受け取ります。
DiffClassStaticMethod.class_meth()

# 出力 => (__main__.DiffClassStaticMethod,)
引数に何も指定しなくても、第一引数に、クラスを受け取っていることがわかります。次に、staticmethodを使ってみます。
# ごく普通のシンプルな関数としてふるまいます
DiffClassStaticMethod.static_meth()

# 出力 => ()
クラスを受け取らず、staticmethodに定義された引数のみ受け取ります。
正直staticmethodは実用的には関数と何ら変わりないので、関数として書いちゃう方がいいと思います。

○classmethodの慣例

上でみたように、classmethodは、第一引数にクラスを受け取ります。なので、それを以下のようにclsとして書くのが慣例らしいです(エラーはでないですが)。
class ClassMethod:
    @classmethod
    def class_meth(cls, *args):
        print(clf)
以上です。