環境設定 数値 文字列 正規表現 リスト タプル 集合 辞書 ループ 関数 クラス データクラス 時間 パス ファイル スクレイピング その他

Python の __slots__ と継承にかかわる不思議な現象:子クラスのインスタンスは子クラスの __slots__ にない変数にアクセスできる

最終更新日 2023.02.18

子クラスに __slots__ を設定しても、そこにないインスタンス変数を定義できます。

class User:
    def __init__(self):
        self.id = 0
        self.name = 'Alice'


class Employee(User):
    __slots__ = 'gender'

    def __init__(self):
        super().__init__()

        self.age = 28


a = User()
b = Employee()

print(a.__dict__)  # {'id': 0, 'name': 'Alice'}
print(b.__dict__)  # {'id': 0, 'name': 'Alice', 'age': 28}

Employee の __slots__ に age はありませんが self.age = 28 と定義されています。これは __slots__ の一般的なふるまいとずれています。

不思議な挙動はこれで終わりません。この記事はすべて Python 3.11.0 で検証しています。

子クラスの slots にない変数をインスタンスは勝手につくれる

class User:
    def __init__(self):
        self.id = 0
        self.name = 'Alice'


class Employee(User):
    __slots__ = 'gender'

    def __init__(self):
        super().__init__()


a = Employee()
a.gender = 'woman'

b = Employee()
b.salary = 50000

print(a.__dict__)  # {'id': 0, 'name': 'Alice'}
print(a.gender)  # woman

print(b.__dict__)  # {'id': 0, 'name': 'Alice'}
print(b.salary)  # 50000

salary は __slots__ にない変数ですが、インスタンス b は勝手につくっています。

親子に slots があると上にあげた「あいまいな判定」はすべて無効になる

class User:
    __slots__ = 'id', 'name'

    def __init__(self):
        self.id = 0
        self.name = 'Alice'


class Employee(User):
    __slots__ = 'gender'

    def __init__(self):
        super().__init__()

        self.salary = 50000


# AttributeError: 'Employee' object has no attribute 'salary'

親子の両方に __slots__ があると、Employee に勝手なインスタンス変数を定義できなくなります。