@classmethod
と@staticmethod
の意味
- メソッドは、オブジェクトの名前空間内の関数であり、属性としてアクセスできます。
- 通常の (インスタンス) メソッドは、インスタンス (通常は と呼びます
self
) を暗黙の最初の引数として取得します。
- クラスメソッドは、クラス (通常は と呼びます
cls
) を暗黙の最初の引数として取得します。
- 静的メソッドは、 (通常の関数のように) 暗黙的な最初の引数を取得しません。
いつ、なぜ使用する必要があり、どのように使用する必要がありますか?
どちらのデコレータも必要ありません。しかし、関数への引数の数を最小限に抑えるべきであるという原則 (Clean Coder を参照) では、関数はまさにそれを行うのに役立ちます。
class Example(object):
def regular_instance_method(self):
"""A function of an instance has access to every attribute of that
instance, including its class (and its attributes.)
Not accepting at least one argument is a TypeError.
Not understanding the semantics of that argument is a user error.
"""
return some_function_f(self)
@classmethod
def a_class_method(cls):
"""A function of a class has access to every attribute of the class.
Not accepting at least one argument is a TypeError.
Not understanding the semantics of that argument is a user error.
"""
return some_function_g(cls)
@staticmethod
def a_static_method():
"""A static method has no information about instances or classes
unless explicitly given. It just lives in the class (and thus its
instances') namespace.
"""
return some_function_h()
インスタンス メソッドとクラス メソッドの両方で、少なくとも 1 つの引数を受け入れないことは TypeError ですが、その引数のセマンティクスを理解していないことはユーザー エラーです。
( を定義some_function
します。例:
some_function_h = some_function_g = some_function_f = lambda x=None: x
これは機能します。)
インスタンスとクラスのドット ルックアップ:
インスタンスのドット ルックアップは、次の順序で実行されます。
- クラス名前空間のデータ記述子 (プロパティなど)
- インスタンス内のデータ
__dict__
- クラス名前空間 (メソッド) の非データ記述子。
インスタンスのドット ルックアップは次のように呼び出されることに注意してください。
instance = Example()
instance.regular_instance_method
そしてメソッドは呼び出し可能な属性です:
instance.regular_instance_method()
インスタンスメソッド
引数 はself
、ドット ルックアップを介して暗黙的に指定されます。
クラスのインスタンスからインスタンス メソッドにアクセスする必要があります。
>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>
クラスメソッド
引数 はcls
、ドット ルックアップによって暗黙的に指定されます。
このメソッドには、インスタンスまたはクラス (またはサブクラス) を介してアクセスできます。
>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>
静的メソッド
引数は暗黙的に与えられません。このメソッドは、ルックアップできることを除けば、モジュールの名前空間で (たとえば) 定義された関数と同じように機能します。
>>> print(instance.a_static_method())
None
繰り返しますが、いつ使用する必要がありますか? なぜ使用する必要があるのですか?
これらはそれぞれ、インスタンス メソッドに対してメソッドに渡す情報を徐々に制限しています。
情報が必要ない場合に使用します。
これにより、関数とメソッドの推論と単体テストが容易になります。
どちらが推論しやすいですか?
def function(x, y, z): ...
また
def function(y, z): ...
また
def function(z): ...
引数が少ない関数ほど、推論が容易になります。また、単体テストも簡単です。
これらは、インスタンス、クラス、および静的メソッドに似ています。インスタンスがある場合、そのクラスもあることに注意してください。もう一度自問してみてください。どちらが推論しやすいでしょうか?
def an_instance_method(self, arg, kwarg=None):
cls = type(self) # Also has the class of instance!
...
@classmethod
def a_class_method(cls, arg, kwarg=None):
...
@staticmethod
def a_static_method(arg, kwarg=None):
...
組み込みの例
以下に、私のお気に入りの組み込みの例をいくつか示します。
str.maketrans
静的メソッドはモジュール内の関数でしたが、名前空間string
からアクセスできる方がはるかに便利です。str
>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'
dict.fromkeys
クラス メソッドは、反復可能なキーからインスタンス化された新しい辞書を返します。
>>> dict.fromkeys('abc')
{'a': None, 'c': None, 'b': None}
サブクラス化すると、クラス情報をクラス メソッドとして取得することがわかります。これは非常に便利です。
>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'>
私のアドバイス - 結論
クラスまたはインスタンスの引数が不要であるが、関数がオブジェクトの使用に関連しており、関数がオブジェクトの名前空間にあると便利な場合は、静的メソッドを使用します。
インスタンス情報は必要ないが、他のクラスや静的メソッド、またはコンストラクターとしてのクラス情報が必要な場合は、クラス メソッドを使用します。(ここでサブクラスを使用できるように、クラスをハードコーディングしないでください。)