Pythonのクラス変数とインスタンス変数
はじめに
Pythonのクラス変数とインスタンス変数の値の持ち方がよくわからなかったので整理する。
登場する用語達
- Human ➡ Humanクラス
- human_name ➡ 変数
- change_name ➡ 名前を書き換えるメソッド
- first_human ➡ インスタンス
- second_human ➡ インスタンス
- init() ➡ 特殊メソッド
Pythonのクラス変数
Pythonのクラス変数は、クラスで保持する変数のため、すべてのインスタンスで共有される。
例えば、下記のコードを例に詳しく見てみる。
class Human:
human_name:str = "Takeshi"
def change_name(self, human_name):
Human.human_name = human_name
first_human = Human()
second_human = Human()
print(first_human.human_name)
print(second_human.human_name)
first_human.change_name("Jiro")
# 区切り線
print("-------------")
print(first_human.human_name)
print(second_human.human_name)
解説
上から区切って解説していく。
まず、Humanクラスを作成し、クラス変数としてhuman_name="Takeshi"を定義
class Human:
# クラス変数
human_name:str = "Takeshi"
# メソッド
def change_name(self, human_name):
Human.human_name = human_name
そして、Humanクラスでインスタンス化
first_human = Human()
second_human = Human()
printでfirst_humanとsecond_humanのhuman_name変数を出力
print(first_human.human_name)
print(second_human.human_name)
出力結果として、クラス変数で指定したTakeshiが出力。
first_humanとsecond_humanはHumanクラスのクラス変数を持つので、Takeshiを出力している。
Takeshi
Takeshi
ここで、first_humanを"Jiro"に変更するchange_nameメソッドを実行する。
first_human.change_name("Jiro")
change_nameメソッドについて
change_nameメソッドは、Humanクラスのクラス変数であるhuman_nameに、引数で受け取った値を格納することでクラス変数であるhuman_nameの値"Takeshi"を変更しようとしている。
def change_name(self, human_name):
Human.human_name = human_name
もう一度、printでfirst_humanとsecond_humanのhuman_name変数を出力
print(first_human.human_name)
print(second_human.human_name)
出力結果は、Jiro Jiro となってしまった。
first_humanのhuman_nameを変更したかったのに、second_humanもJiroとなってしまった。
Jiro
Jiro
原因
原因は、first_humanとsecond_humanが共通のクラス変数、human_nameを参照していたから。
図1:初期状態
初期状態では、クラス変数の値"Takeshi"を持っているが、change_nameメソッドでクラス変数の値を"Jiro"に変更することで、参照しているすべてのインスタンスに影響を及ぼしてしまう。
図2:change_nameメソッド実行
Pythonのインスタンス変数
Pythonのインスタンス変数は、インスタンス単位で保持する固有の変数となっている。
例えば、下記のコードを例に詳しく見てみる。
class Human:
def __init__(self, human_name="Takeshi"):
self.human_name = human_name
def change_name(self, human_name):
self.human_name = human_name
first_human = Human()
second_human = Human()
print(first_human.human_name)
print(second_human.human_name)
first_human.change_name("Jiro")
# 区切り線
print("-------------")
print(first_human.human_name)
print(second_human.human_name)
解説
上から区切って解説していく。
まず、Humanクラスを作成し、特殊メソッド__init__()内にインスタンス変数を定義しデフォルト値に"Takeshi"を設定。
__init__()について
init()はインスタンスを初期化するPythonの特殊メソッドとなっている。インスタンス変数は、この特殊メソッド内で定義する。また、selfを使用して引数を与える。
class Human:
# インスタンス変数
def __init__(self, human_name:str="Takeshi"):
self.human_name = human_name
def change_name(self, human_name):
self.human_name = human_name
Humanクラスでインスタンス化
first_human = Human()
second_human = Human()
printでfirst_humanとsecond_humanのhuman_name変数を出力
print(first_human.human_name)
print(second_human.human_name)
出力結果として、インスタンス変数で指定したデフォルト値のTakeshiが出力。
Takeshi
Takeshi
first_humanを"Jiro"に変更するchange_nameメソッドを実行する。
first_human.change_name("Jiro")
printでfirst_humanとsecond_humanのhuman_name変数を出力
print(first_human.human_name)
print(second_human.human_name)
出力結果は、Jiro Takeshi となった。
Jiro
Takeshi
理由
クラス変数が共通なのに対して、インスタンス変数はインスタンスごとに固有であるため、値の保持が可能になる。
図3:初期化
インスタンス化時に初期化されたインスタンス変数のデフォルト値"Takeshi"を受け取るが、change_nameメソッド実行によって書き換わるのは、first_humanのインスタンス変数のみとなり、Jiroに書き換わる。
図4:change_nameメソッド実行
まとめ
- クラス変数は共通
- インスタンス変数は固有
Discussion