Pythonでオブジェクト指向プログラミング(OOP)を学び始めると、クラス変数という言葉をよく耳にします。
最初は「インスタンス変数と何が違うの?」と疑問に思う方も多いでしょう。
クラス変数は、クラス全体で共有されるデータを扱うための仕組みです。インスタンスごとに異なる値を持つ「インスタンス変数」とは異なり、クラス変数はすべてのインスタンスで共通の値を持ちます。
今回は、Pythonを使ってクラス変数の基本的な概念や使い方を、初心者でも理解できるように丁寧に解説します。
クラス変数は、その名の通り「クラスに属する変数」です。
クラスから作られるすべてのインスタンスに共通して使われるため、クラス全体でデータを共有したいときに便利です。
例えば、犬を表す Dog クラスを作るとします。犬という生き物の「種(species)」は共通しているので、インスタンスごとにバラバラに持たせる必要はありません。そこでクラス変数を使えば、1つ定義しておくだけで全てのインスタンスからアクセスできるのです。
クラス変数を理解するには、インスタンス変数との違いを押さえておくことが大切です。
クラス変数は、クラスの中でメソッドの外側に書くことで定義できます。
以下のコードを見てみましょう。
class Dog:
# クラス変数(全てのインスタンスで共有される)
species = "Canis familiaris"
def __init__(self, name, age):
# インスタンス変数(インスタンスごとに異なる)
self.name = name
self.age = age
def bark(self):
return f"{self.name} says woof!"
# インスタンスを作成
dog1 = Dog("Buddy", 3)
dog2 = Dog("Lucy", 5)
# クラス名を使ってアクセス
print(Dog.species) # 出力: Canis familiaris
# インスタンスからアクセス
print(dog1.species) # 出力: Canis familiaris
print(dog2.species) # 出力: Canis familiaris
# クラス名を通じて変更すると全インスタンスに反映される
Dog.species = "Canis lupus familiaris"
print(dog1.species) # 出力: Canis lupus familiaris
print(dog2.species) # 出力: Canis lupus familiaris
ここでは、species がクラス変数、name と age がインスタンス変数です。
つまり、Buddy という犬と Lucy という犬は名前や年齢は違いますが、「犬という種」は同じなので species を共有している、というイメージです。
クラス変数は、クラス名を通じて直接アクセスすることも、インスタンスを通じてアクセスすることもできます。ただし、インスタンスを通じてアクセスした場合でも、クラス変数はすべてのインスタンスで共有されます。
このコードからわかる通り、species はクラス全体で1つだけ存在しており、クラス名を通じて変更すると全てのインスタンスに反映されます。
クラス変数を使用して、クラス全体で共有される情報を管理する例を見てみましょう。例えば、Dog クラスのインスタンス数を追跡する場合です。
class Dog:
# クラス変数
species = "Canis familiaris"
number_of_dogs = 0 # インスタンス数を追跡するクラス変数
def __init__(self, name, age):
self.name = name # インスタンス変数
self.age = age # インスタンス変数
self.__class__.number_of_dogs += 1 # 新しいインスタンスが生成されるたびにカウント
#Dog.number_of_dogs += 1 # このようにも書けるがシミュレータだとエラー
def bark(self):
return f"{self.name} says woof!"
# Dogクラスのインスタンスを生成
dog1 = Dog("Buddy", 3)
dog2 = Dog("Lucy", 5)
dog3 = Dog("Max", 2)
# クラス変数 number_of_dogs にアクセス
print(f"現在の犬の数: {Dog.number_of_dogs}") # 出力: 現在の犬の数: 3
新しいインスタンスが作られるたびに number_of_dogs が増える仕組みになっており、全体の数を簡単に追跡できます。
クラス変数にリストや辞書など「変更可能(ミュータブル)」なデータ型を使うと、全インスタンスで同じオブジェクトを共有してしまうため注意が必要です。
class Dog:
tricks = [] # クラス変数としてリストを定義
def __init__(self, name):
self.name = name # インスタンス変数
def add_trick(self, trick):
self.tricks.append(trick) # クラス変数を変更
# Dogクラスのインスタンスを生成
dog1 = Dog("Buddy")
dog2 = Dog("Lucy")
# 各インスタンスで芸(トリック)を追加
dog1.add_trick("roll over")
dog2.add_trick("play dead")
# クラス変数 tricks を確認
print(Dog.tricks) # 出力: ['roll over', 'play dead']
print(dog1.tricks) # 出力: ['roll over', 'play dead']
print(dog2.tricks) # 出力: ['roll over', 'play dead']
どちらの犬も同じ tricks リストを共有しているため、1匹が覚えた芸が全員に反映されてしまいます。 このように予期せぬ挙動になることがあるので、ミュータブルなクラス変数を使う場合は設計に注意しましょう。
最後に、学生の総数を管理する Student クラスの例を見てみましょう。
class Student:
# クラス変数
total_students = 0
def __init__(self, name, grade):
self.name = name
self.grade = grade
self.__class__.total_students += 1 # インスタンスが作られるたびにカウント
#Student.total_students += 1 # インスタンスが作られるたびにカウント
def display_info(self):
print(f"学生名: {self.name}, 成績: {self.grade}")
@classmethod
def get_total_students(cls):
return cls.total_students
# インスタンス生成
student1 = Student("佐藤", "A")
student2 = Student("鈴木", "B")
student3 = Student("高橋", "A")
# 各インスタンスの情報を表示
student1.display_info()
student2.display_info()
student3.display_info()
# クラスメソッドを使って総学生数を確認
print(f"総学生数: {Student.get_total_students()}") # 出力: 総学生数: 3
ここでは、total_students というクラス変数が全体の学生数を管理しています。 新しい学生が作られるたびに自動的に数が増えていくので、外部で数を数える必要がありません。
また、get_total_students というクラスメソッドを用意することで、クラス変数を安全に取得できるようにしています。
今回はクラス変数について学習しました。 クラス変数は、クラス全体で共有したい情報を扱うための仕組みです。
という違いを理解しておけば、コードの設計がぐっとわかりやすくなります。
インスタンス数の管理や共通の設定値の保持など、クラス変数の使いどころは多くあります。ただし、リストや辞書といったミュータブルなデータ型を扱う際は、思わぬ副作用が起きないように注意してください。