【Python】クラス継承時のクラス変数とインスタンス変数

Pythonのオブジェクト指向プログラミングでは、クラス変数とインスタンス変数の使い分けが重要です。特に、クラスの継承時にはこれらの変数の扱い方に注意が必要です。この記事では、クラス変数とインスタンス変数の基本、クラスの継承方法、そしてクラス変数の扱い方の注意点について、サンプルコードを交えて解説します。

1. クラス変数とインスタンス変数

まず、クラス変数とインスタンス変数の違いを理解しましょう。

クラス変数

クラス変数は、クラスそのものに属する変数です。すべてのインスタンスが共有する値を保持します。

class Dog:
    species = "Canis familiaris"  # クラス変数

    def __init__(self, name, age):
        self.name = name  # インスタンス変数
        self.age = age    # インスタンス変数

# インスタンス生成
dog1 = Dog("Buddy", 2)
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(Dog.species)  # Canis lupus familiaris
print(dog1.species)  # Canis lupus familiaris
print(dog2.species)  # Canis lupus familiaris

インスタンス変数

インスタンス変数は、各インスタンスごとに異なる値を持つ変数です。

print(dog1.name)  # Buddy
print(dog2.name)  # Lucy

dog1.name = "Max"
print(dog1.name)  # Max
print(dog2.name)  # Lucy

2. クラスの継承

クラスの継承は、新しいクラスが既存のクラスの特性を引き継ぐことを可能にします。親クラスのクラス変数とインスタンス変数をどのように扱うかがポイントです。

継承の基本

class Animal:
    species = "Unknown"  # クラス変数

    def __init__(self, name):
        self.name = name  # インスタンス変数

class Dog(Animal):
    species = "Canis familiaris"

dog = Dog("Buddy")
print(dog.species)  # Canis familiaris
print(dog.name)     # Buddy

クラス変数の注意点

クラス変数はすべてのインスタンスで共有されるため、継承時に意図しない動作を引き起こすことがあります。

class Parent:
    shared = []

    def __init__(self, value):
        self.shared.append(value)

class Child(Parent):
    pass

p1 = Parent(1)
c1 = Child(2)

print(p1.shared)  # [1, 2]
print(c1.shared)  # [1, 2]

# クラス変数の独立性を保つ方法
class Parent:
    def __init__(self, value):
        if not hasattr(self.__class__, 'shared'):
            self.__class__.shared = []
        self.__class__.shared.append(value)

p2 = Parent(3)
c2 = Child(4)

print(p2.shared)  # [3]
print(c2.shared)  # [4]

3. まとめ

クラス変数とインスタンス変数の違いを理解し、クラスの継承時にそれぞれを適切に扱うことが重要です。クラス変数はすべてのインスタンスで共有されるため、継承時に予期せぬ動作を引き起こすことがあります。適切な初期化方法を用いることで、この問題を回避することができます。クラス変数の独立性を保つためには、hasattr関数を使って初期化する方法が有効です。

hasattr関数を使って初期化する方法については以下の記事を参照してください。