今見たように、クラス変数は、クラスと関連付けられた変数だった。これと同様に、クラスと関連付けられた関数(メソッド)もある。また、最後に取り上げるクラスともインスタンスとも関連のないスタティックメソッドもある。これらについてもその特徴を最初にまとめておこう。
メソッドの種類 | 説明 | 第1パラメーター | 呼び出し方 |
---|---|---|---|
インスタンスメソッド | インスタンスのデータを使って何らかの処理を行う | 呼び出しに使ったインスタンスがselfにセットされる | インスタンス.インスタンスメソッド(引数) クラス.インスタンスメソッド(インスタンス, 引数) |
クラスメソッド | クラスに関連した処理を行う | 呼び出しに使ったクラスがclsにセットされる | クラス.クラスメソッド(引数) インスタンス.クラスメソッド(引数) |
スタティックメソッド | クラスともインスタンスとも関連のない処理を行う | 特定のクラスやインスタンスはセットされない | クラス.スタティックメソッド(引数) インスタンス.スタティックメソッド(引数) |
インスタンスメソッド/クラスメソッド/スタティックメソッド |
クラスメソッドはインスタンスではなく「クラス」と関連付けられている。通常、インスタンスメソッドは「インスタンス.インスタンスメソッド(引数)」のようにして呼び出すが(例:"foo".upper())*1、クラスメソッドは「クラス.クラスメソッド(引数)」のようにして呼び出す。そのため、クラスメソッドは「そのクラスのインスタンスがなくても呼び出せる」ことが、インスタンスメソッドとの大きな違いだ。
*1 実際にはインスタンスメソッドは、クラスを利用して、「クラス.インスタンスメソッド(呼び出しに使用するインスタンス, 引数)」のようにしても呼び出せる。これは「呼び出しに使用するインスタンス.インスタンスメソッド(引数)」と同じことになる。
また、クラスメソッドは「インスタンス.クラスメソッド(引数)」という形でも呼び出せる。ただし、この方式でクラスメソッドを呼び出すときには、第1パラメーターには「クラス自体」が渡される。
クラスメソッドを定義するには幾つかの方法があるが、まず「@classmethod」デコレーターを利用する方法を紹介しよう。「デコレーター」とは「関数やメソッド、クラスの定義の前に置く(デコレート、修飾する)ことで、その性質をカスタマイズする」ために使えるものだ。
以下に例を示す。
class MyClass:
count = 0
def __init__(self):
MyClass.count += 1
print(f'you made {MyClass.count} instance(s)')
@classmethod # クラスメソッドの定義
def get_count(cls):
print(cls.count) # クラス変数には「cls.クラス変数」としてアクセス
上に示したように、クラスメソッドを定義するには、def文によるメソッド定義の前に「@classmethod」と書くだけだ。クラスメソッドの第1パラメーターの名前は「cls」とすることが推奨されている。既に述べた通り、クラスメソッドを呼び出すには「クラス.クラスメソッド(引数)」「インスタンス.クラスメソッド(引数)」とするが、これらの呼び出しにより、第1パラメーターの「cls」には呼び出しに使われたクラス、または呼び出しに使われたインスタンスのクラスが渡される。
クラスメソッドの内部からクラス変数にアクセスするには、get_countクラスメソッドの内部で行っているように、第1パラメーターの「cls」を介して「cls.クラス変数」とする。
では、実際にクラスメソッドを呼び出してみよう。まずはクラスを使って呼び出してみる。
MyClass.get_count()
実行結果を以下に示す。インスタンスを介さずとも、メソッドを呼び出せていることに注目されたい。
次にインスタンスを介して、クラスメソッドを呼び出してみる。
instance1 = MyClass()
instance2 = MyClass()
instance2.get_count()
この場合も問題なく、クラスメソッドが呼び出されているのが分かる。
「@classmethod」デコレーターではなく、組み込みの「classmethod」関数を使った方法も簡単に紹介しておこう。実際には、これらは書き方が異なるだけで、ほぼ同じことをしている。
class MyClass:
count = 0
def __init__(self):
MyClass.count += 1
print(f'you made {MyClass.count} instance(s)')
@classmethod # クラスメソッドの定義
def get_count(cls):
cls.another_get_count()
another_get_count = classmethod(lambda cls: print('count:', cls.count))
classmethod関数の引数として関数を渡すと、それをクラスメソッドに変換したものが返される(ここでは関数としてラムダ式を渡している)。これをクラス変数(another_get_count)に代入すれば、それを介してクラスメソッドを呼び出せるようになる。
ここでは、get_countクラスメソッドのコードも変更して、第1パラメーターの「cls」を介して、今定義したanother_get_countクラスメソッドを呼び出すようにしている(クラス内のインスタンスメソッドやクラスメソッドからクラスメソッドを呼び出すには、これまでと同様、第1パラメーターの「self」や「cls」を利用する必要がある)。
実際に呼び出してみよう。
MyClass.another_get_count()
instance1 = MyClass()
instance1.another_get_count()
instance1.get_count()
実行結果を以下に示す。
クラスメソッドの使い方としては、「クラス名()」呼び出しでインスタンスを生成するのではなく、クラスメソッド内部で何らかの処理(ネットワークからのデータの読み取りなど)を行った上でそのクラスのインスタンスを作成することが考えられる(そのようなメソッドを「ファクトリメソッド」と呼ぶ)。クラスメソッドをそのようにして使っている例としてはdatetimeモジュールが公開しているdateクラスのtodayメソッドなどがある(小規模なプログラムを書いている間はクラスメソッドを利用することはそれほど多くはないだろう)。
クラスメソッドの詳細についてはPythonのドキュメント「標準型の階層」以下にある「呼び出し可能型」なども参照してほしい。メソッド呼び出し時の詳細な動作などがまとめられている。
インスタンスメソッドはインスタンスと、クラスメソッドはクラスと関連付けられていたが、スタティックメソッド(静的メソッド)はインスタンスともクラスとも関連付けられていないメソッドだ。このメソッドの第1パラメーターには「self」も「cls」も必要ない。
スタティックメソッドは、クラスメソッドと同様に「@staticmethod」デコレーターか組み込みの「staticmethod」関数を使用して定義する。
以下に例を示す(ここでは@staticmethodデコレーターだけを使用した)。
class MyClass:
count = 0
def __init__(self):
MyClass.count += 1
print(f'you made {MyClass.count} instance(s)')
@classmethod # クラスメソッドの定義
def get_count(cls):
cls.another_get_count()
another_get_count = classmethod(lambda cls: print('count:', cls.count))
@staticmethod
def static_get_count():
print('count:', MyClass.count)
クラスメソッドと同様に、スタティックメソッドを定義するには、メソッドの前に「@staticmethod」を前置するだけだ。このとき、第1パラメーターでクラスやインスタンスを受け取る必要はない。そのため、クラス変数をスタティックメソッド内で参照するには「クラス.クラス変数」とするしかない。また、特定のインスタンスと結び付いているわけでもないので、インスタンス変数にアクセスすることもできない(もちろん、メソッドのパラメーターとして、何らかのクラスのインスタンスを受け取った場合は別だ)。
以下に呼び出し例を示す。
MyClass.static_get_count()
instance = MyClass()
instance.static_get_count()
クラスメソッドと同様に、スタティックメソッドは「クラス.スタティックメソッド(引数)」「インスタンス.スタティックメソッド(引数)」のようにして呼び出せる。ただし、既に何度もいっているが、このような呼び出し方をするからといって、何らかのクラスやインスタンスがスタティックメソッドに渡されるわけではないことに注意しよう。
実行結果は以下の通りだ。
スタティックメソッドは、クラスにもインスタンスにも関連付けられていないので、「特定のクラス(やそのインスタンス)と直接は関連しないが、それでもクラスの属性にまとめておくのが適切」な処理をメソッドとして記述するためのものといえる(例えば、長大なメソッドを短いメソッドに分割した結果として得られる、クラスやインスタンスとは無関係な処理を行うプライベートなヘルパーのようなものが考えられるが、そのようにする頻度はそれほど高くないだろう)。
今回はクラスが持つ、3つの属性であるクラス変数、クラスメソッド、スタティックメソッドについて取り上げた。次回は、実際に何かのクラスを作ってみよう。
インスタンス変数とクラス変数の違いを以下の表にまとめる。
変数の種類 | 説明 | クラス外部からのアクセス方法 | クラス内でのアクセス方法 |
---|---|---|---|
インスタンス変数 | インスタンスが持つ データを保存 |
インスタンス.インスタンス変数 | self.インスタンス変数 |
クラス変数 | クラスが持つデータ や複数のインスタン スで共有するデータを保存 |
クラス.クラス変数 インスタンス.クラス変数 |
self.クラス変数(値の書き換えに注意) クラス.クラス変数 self.__class__.クラス変数 type(self).クラス変数 |
インスタンス変数とクラス変数 |
インスタンスメソッド、クラスメソッド、スタティックメソッドの違いを以下の表にまとめる。
メソッドの種類 | 説明 | 第1パラメーター | 呼び出し方 |
---|---|---|---|
インスタンスメソッド | インスタンスのデータを使って何らかの処理を行う | 呼び出しに使ったインスタンスがselfにセットされる | インスタンス.インスタンスメソッド(引数) クラス.インスタンスメソッド(インスタンス, 引数) |
クラスメソッド | クラスに関連した処理を行う | 呼び出しに使ったクラスがclsにセットされる | クラス.クラスメソッド(引数) インスタンス.クラスメソッド(引数) |
スタティックメソッド | クラスともインスタンスとも関連のない処理を行う | 特定のクラスやインスタンスはセットされない | クラス.スタティックメソッド(引数) インスタンス.スタティックメソッド(引数) |
インスタンスメソッド/クラスメソッド/スタティックメソッド |
「Python入門」
Copyright© Digital Advantage Corp. All Rights Reserved.