2015年12月21日

__slots__で軽量化

クラスの先頭で __slots__ を定義することで、クラスを軽量・高速にすることができます。


以下は、一般的なクラス定義です。

>>> class Point(object):
...     def __init__(self):
...         self.x = 0
...         self.y = 0
...
# クラスのサイズを取得
>>> import sys
>>> sys.getsizeof(Point)
492
このクラスのサイズは 492バイトとなりました。

続いて、クラスの先頭で __slots__ を定義します。__slots__ はリスト(or タプル)で、各要素は使用するアトリビュート名とします。

>>> class Point(object):
...     __slots__ = ['x', 'y']  # __slots__を定義
...
...     def __init__(self):
...         self.x = 0
...         self.y = 0
...
# クラスのサイズを取得
>>> sys.getsizeof(Point)
428
クラスのサイズは 428バイトになりました。
この例では __slots__ を定義することで 64バイト減ったことになります。インスタンスが多数ある場合には(100万個とか)、決して小さくはない減少量と言えます。


ただし、__slots__ には副作用もあります。
通常のクラスではアトリビュートを後から追加することができますが、__slots__ を定義した場合には、__slots__ の要素のアトリビュートしか持てないようになります。

# __slot__を定義したクラスのインスタンス
>>> p = Point()

# zを追加できない
>>> p.z = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'

# setattrでもできない
>>> setattr(p, 'z', 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in 
AttributeError: 'Point' object has no attribute 'z'

一方、__slots__ を定義することで、アトリビュートの追加が出来ない代わりに、アトリビュートへのアクセスが高速化されるという利点もあります。



結局、__slots__ を使うべき場面とは、以下の2つを共に満たす場合になります。

  • アトリビュートの追加がないクラス
  • 多数のインスタンスが予想されるクラス、又は、動作速度が非常に重要なクラス

0 件のコメント:

コメントを投稿