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 に勝手なインスタンス変数を定義できなくなります。