はじめに #
Pythonのクラスにおいて、インスタンスメソッドとクラスメソッド (classmethod), スタティックメソッド (staticmethod) を使い分ける方法をまとめました。以下のように使い分けます。
- インスタンスメソッド:インスタンス変数にアクセスする
- クラスメソッド:インスタンス変数にアクセスせず、クラス変数にアクセスする
- スタティックメソッド:インスタンス変数とクラス変数のどちらもアクセスしない
詳細は以下で述べます。
検証環境:Python 3.9.7
メソッドの使い分け #
まず各メソッドの使い分けについて、以下のMyClassクラスを例に示します。
class MyClass:
CONST = 10 # クラス変数
def __init__(self, x):
self.x = x # インスタンス変数
def add0(self, y, z):
# インスタンスメソッド
return self.x + y + z
@classmethod
def add1(cls, y, z):
# クラスメソッド
return cls.CONST + y + z
@staticmethod
def add2(y, z):
# スタティックメソッド
return y + z
xはインスタンスごとに設定される変数で、インスタンス変数と呼びます。また、CONSTはMyClass自体に設定されるので、クラス変数と呼びます。
add0メソッドのようにインスタンス変数にアクセスするメソッドは、インスタンスメソッドとして定義します。インスタンスメソッドの第1引数には、selfを取ります。selfはインスタンス自身を指す変数です(慣習的にselfを使っているだけで、Pythonの予約語ではありません)。
一方、add1メソッドのようにインスタンス変数は使用せず、クラス変数にのみアクセスするものは、@classmethodを付けて、クラスメソッドとして定義します。クラスメソッドの第1引数には、clsを取ります。clsはクラス自身を指す変数です(こちらも慣習的にclsを使っているだけで、Pythonの予約語ではありません)。
最後に、add2メソッドのように、インスタンス変数もクラス変数も使用しないものは、@staticmethodを付けて、スタティックメソッドとして定義します。スタティックメソッドの引数には、必要な変数のみ定義します。
なお、__init__はインスタンスの生成時に実行されるメソッドで、コンストラクタと呼びます。また、@classmethodや@staticmethodは関数を装飾する仕組みであり、デコレータと呼びます。
クラスメソッド #
各インスタンスはクラス変数にアクセスできるため、クラスメソッドは以下のようにインスタンスメソッドとして記述することも出来ます。
def add1(self, y, z):
return self.CONST + y + z
ただし、インスタンス変数にアクセスしない(インスタンス変数を変更しない)ことを明示するには、最初の例のように@classmethodを付けた方が良いでしょう。
スタティックメソッド #
一方、スタティックメソッドとして定義可能なメソッドは、以下のようなインスタンスメソッドとして書くことも可能です。
def add2(self, y, z):
return y + z
しかし、PyLintで構文チェックを掛けると、以下のリファクタリングのメッセージが出力されます。
R0201 (no-self-use) Method could be a function
すなわち、「メソッドは関数として定義できる」という意味です。クラスの外にadd2メソッドを出してもロジック上は問題ないのですが、MyClassクラスの他のメソッドからadd2を呼び出している場合などは、クラスから出したくないこともあります。
そこで@staticmethodを付け、スタティックメソッドとして定義することで、PyLintのメッセージを出力しないようにできます。
また、@staticmethodを付けたご利益として、以下のようにインスタンスを定義せずにメソッドを使用できます。
>>> MyClass.add2(1, 2)
3
もしadd2をインスタンスメソッドとして定義すると、以下のように一度インスタンスを作った上でメソッドを実行する必要があります。
>>> my_class = MyClass(3)
>>> my_class.add2(1, 2)
3