Python のクラスメソッドとスタティックメソッド



クラスメソッドは、インスタンスオブジェクトを操作することができないメソッドです。

スタティックメソッドは、インスタンスオブジェクトもクラスオブジェクトも操作することができないメソッドです。



インスタンス
オブジェクト
クラス
オブジェクト
メソッド操作できる操作できる
クラスメソッド操作できない操作できる
スタティックメソッド操作できない操作できない

どうやって定義するの?

クラスメソッド、スタティックメソッドを定義するには @classmethod , @staticmethod を書き加えます。@ はデコレータです。デコレータについては後述します。

classmethod デコレータ

第一引数にメソッドを呼び出してきたインスタンスオブジェクトではなくクラスオブジェクトを代入します。クラスメソッドの第一引数には、いつも cls を使ってください。

class C(object):
    # 第一引数にクラスオブジェクトが代入される。
    @classmethod
    def cls_method(cls):
        print(cls.__name__)

o = C()
o.cls_method()  # C


クラスメソッドの第一引数は cls を使うように PEP 8 で定められています。

関数とメソッドの仮引数
Function and Method Arguments

インスタンスメソッドの第一引数には、いつも self を使ってください。
Always use self for the first argument to instance methods.

クラスメソッドの第一引数には、いつも cls を使ってください。
Always use cls for the first argument to class methods.

もし関数の仮引数名に予約語と同じものを使いたい場合は、スペルを省略したり変更したりするのではなく、仮引数名の末尾にアンダースコアを1文字追加してください。つまり clss とか klass とか書くよりも class_ と書いた方が良いです。(もしかしたら、予約語を使いたい時は、同義語を使った方が、より良いやり方かもしれません。)
If a function argument's name clashes with a reserved keyword, it is generally better to append a single trailing underscore rather than use an abbreviation or spelling corruption. Thus class_ is better than clss. (Perhaps better is to avoid such clashes by using a synonym.)
PEP 8 - Style Guide for Python Code

staticmethod デコレータ

第一引数にメソッドを呼び出してきたインスタンスオブジェクトを代入することを省略します。 staticmethod デコレータは、そのクラスでしか使わない関数を定義する時に使います。

class C(object):
    def method(self):
        self.stc_method()
    
    # クラス C  でしか使われない関数なんやな...
    # 第一引数を省略できる。
    @staticmethod
    def stc_method():
        print('Hello, world!')

o = C()
o.method()  # Hello, world!


もちろん、たんなる関数なので、クラス直下ではなくモジュール直下で定義することもできます。 しかし、モジュール直下で関数を定義してしまうと "モジュール内で定義されたクラスや関数から呼び出される汎用的な関数なんやな" と読む側に誤解を与えてしまいます。

# モジュール全体で使う関数なんやな...
def stc_method():
    print('Hello, world!')

意味があるの?

〜することができないようにする機能と言うのは、意味があるのでしょうか?単純に self を使わなければいいじゃんと言うだけの話かもしれません。

ただし、メソッドがどう言った性質であるかを示す可読性の面でのメリット、想定外の改修を加えさせないメンテナンス面でのメリットがあるかなと思ったりもします。

〜しないと決めると可読性があがるので、とても素晴らしい機能だと個人的には思っています。

どんな風に実装されているの?

簡単には...

classmethod も staticmethod も組み込み関数として最初から付いてきているので自分で実装することはありませんが、このような classmethod, staticmethod は、クロージャを使えば簡単に実装することができます。クロージャについては後述します。

def classmethod(method):
    def decorated_method(self, *args, **kwargs):
        return method(type(self), *args, **kwargs)
    return decorated_method
def staticmethod(method):
    def decorated_method(self, *args, **kwargs):
        return method(*args, **kwargs)
    return decorated_method

正確には...

スタティックメソッドも、クラスメソッドも、クロージャディスクリプタを組み合わせて実装されているらしいです。ディスクリプタは難しいので Effecvtive Python を買って 4 章を読んで見てください。たぶん、これが一番わかりやすいです。

class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, klass=None):
        if klass is None:
            klass = type(obj)
        def newfunc(*args):
            return self.f(klass, *args)
        return newfunc
class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f

デコレータとクロージャって何?

デコレータは、複数の関数、メソッドに共通した前処理と後処理を追加したいときに使います。クロージャ(関数閉包)とは、閉じて包まれた名前空間を持った関数です。