通常、class
文によって定義されたクラスのインスタンスは自由にデータメンバを追加することができ、属性の値は__dict__
メンバ変数に保存されていますが、クラスに__slots__
を定義し、明示的にデータメンバを列挙すると、そのクラスのインスタンスは、__dict__
を持たなくなり、列挙されていないデータメンバを持てなくなります。
__dict__
を持たない分、メモリが節約でき、またデータメンバへのアクセスも若干早くなります。
ただ、__slots__
を定義する際、いくつか注意点があります。
(参考: https://docs.python.org/3/reference/datamodel.html#slots)
1. クラスに__slots__
を定義する場合、その基底クラスは全て__slots__
を持つ必要がある。
次の例のように、基底クラスに__slots__
が定義されていないと、__dict__
が生成されてデータメンバが追加可能になり、__slots__
を定義した意味が無くなります。
class A:
pass
class B(A):
__slots__ = ('foo',)
>>> b = B()
>>> b.bar = 3 # __slots__を定義しているにも関わらず属性が定義できてしまう
>>> b.__dict__ # __dict__が生成されてしまっている
{'bar': 3}
基底クラスに__slots__
を定義すると、この問題は解決します。
class A:
__slots__ = () # 空の__slots__を定義する
class B(A):
__slots__ = ('foo',)
>>> b = B()
>>> b.bar = 3
AttributeError: 'B' object has no attribute 'bar'
>>> b.__dict__
AttributeError: 'B' object has no attribute '__dict__'
2. 基底クラスの__slots__
に同名の変数の定義があると、重複してデータメンバが確保されていまう。
無駄なアクセスできない変数が生成されてまいます。将来的にはエラーとなるようチェックをかけるかもしれないということですが、Python 3.7時点ではエラーにはなりません。
class A:
__slots__ = ('foo',)
class B(A):
__slots__ = ('foo',)
>>> import sys
>>> a = A()
>>> b = B()
>>> sys.getsizeof(a)
48
>>> sys.getsizeof(b)
56
3. 多重継承により、空でない__slots__
を持つ複数の基底クラスは持てない。
この制約はエラーになるのですぐにわかりますが、下記のコードを実行するとTypeError: multiple bases have instance lay-out conflict
となります。
class A:
__slots__ = ('foo',)
class B:
__slots__ = ('bar',)
class C(A, B):
__slots__ = ('baz',)
次のように、どちらか一方は空の__slots__
となるようにクラスの設計を考え直す必要があります。
class A:
__slots__ = ()
class B:
__slots__ = ('foo', 'bar')
class C(A, B):
__slots__ = ('baz',)
参考: https://stackoverflow.com/questions/472000/usage-of-slots