Pythonの目的は何ですか?__slots__
特に、いつ使用したいのか、使用したくないのかについてはどうでしょうか。
11 に答える
Pythonでは、これの目的は
__slots__
何ですか?また、これを避けるべきケースは何ですか?
TLDR:
特別な属性__slots__
を使用すると、オブジェクト インスタンスが持つと予想されるインスタンス属性を明示的に指定し、期待される結果を得ることができます。
- より高速な属性アクセス。
- メモリのスペース節約。
スペースの節約は
- の代わりにスロットに値の参照を格納します
__dict__
。 - 親クラスがそれらを拒否し、宣言した場合の拒否
__dict__
と作成。__weakref__
__slots__
クイック警告
小さな注意点として、継承ツリーで特定のスロットを 1 回だけ宣言する必要があります。例えば:
class Base:
__slots__ = 'foo', 'bar'
class Right(Base):
__slots__ = 'baz',
class Wrong(Base):
__slots__ = 'foo', 'bar', 'baz' # redundant foo and bar
これを間違えても Python は反対しません (おそらくそうするべきです)。それ以外の場合は問題が発生しない可能性がありますが、オブジェクトは必要以上に多くのスペースを占有します。Python 3.8:
>>> from sys import getsizeof
>>> getsizeof(Right()), getsizeof(Wrong())
(56, 72)
これは、Base のスロット記述子に、Wrong のスロットとは別のスロットがあるためです。これは通常は発生しないはずですが、次の可能性があります。
>>> w = Wrong()
>>> w.foo = 'foo'
>>> Base.foo.__get__(w)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: foo
>>> Wrong.foo.__get__(w)
'foo'
最大の注意点は、複数の継承に関するものです。複数の「空でないスロットを持つ親クラス」を結合することはできません。
この制限に対応するには、ベスト プラクティスに従ってください: 1 つまたはすべてを除くすべての親の抽象化を、それぞれの具象クラスと新しい具象クラスが集合的に継承する要素から除外します - 抽象化に空のスロットを与えます (標準ライブラリ)。
例については、以下の多重継承のセクションを参照してください。
要件:
in という名前の属性を
__slots__
ではなく実際にスロットに格納するには__dict__
、クラスを継承するobject
必要があります (Python 3 では自動ですが、Python 2 では明示的でなければなりません)。の作成を防ぐには、継承
__dict__
する必要object
があり、継承内のすべてのクラスを宣言する必要が__slots__
あり、それらのいずれも'__dict__'
エントリを持つことはできません。
読み続けたい場合は、多くの詳細があります。
使用する理由__slots__
: 属性アクセスの高速化。
Python の作成者である Guido van Rossum は、実際には属性アクセスを高速化するために作成したと述べています。__slots__
測定可能なほど大幅な高速アクセスを実証するのは簡単です。
import timeit
class Foo(object): __slots__ = 'foo',
class Bar(object): pass
slotted = Foo()
not_slotted = Bar()
def get_set_delete_fn(obj):
def get_set_delete():
obj.foo = 'foo'
obj.foo
del obj.foo
return get_set_delete
と
>>> min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085
スロット アクセスは、Ubuntu 上の Python 3.5 でほぼ 30% 高速です。
>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342
Windows 上の Python 2 では、約 15% 高速であると測定しました。
使用する理由__slots__
: メモリの節約
のもう 1 つの目的は__slots__
、各オブジェクト インスタンスが占有するメモリ領域を削減することです。
ドキュメントへの私自身の貢献は、この背後にある理由を明確に述べています。
使用するよりも節約されるスペースは
__dict__
重要です。
SQLAlchemy は、大量のメモリ節約を__slots__
.
これを確認するには、Ubuntu Linux で Python 2.7 の Anaconda ディストリビューションを使用してguppy.hpy
(別名 heapy) とsys.getsizeof
を使用します。宣言されていないクラス インスタンスのサイズ__slots__
は 64 バイトです。は含まれません__dict__
。Python の遅延評価に感謝します。__dict__
は参照されるまで呼び出されないようですが、通常、データのないクラスは役に立ちません。存在するように呼び出されると、__dict__
属性はさらに 280 バイト以上になります。
対照的に、(データなし) と__slots__
宣言されたクラス インスタンス()
はわずか 16 バイトであり、スロット内の 1 つの項目で合計 56 バイト、2 つの項目で 64 バイトです。
64 ビット Python の場合、Python 2.7 と 3.6 のメモリ消費量をバイト単位で示します。3.6 で dict が大きくなる各ポイント (0、1、および 2 属性を除く)__slots__
と(スロットは定義されていません) について:__dict__
Python 2.7 Python 3.6
attrs __slots__ __dict__* __slots__ __dict__* | *(no slots defined)
none 16 56 + 272† 16 56 + 112† | †if __dict__ referenced
one 48 56 + 272 48 56 + 112
two 56 56 + 272 56 56 + 112
six 88 56 + 1040 88 56 + 152
11 128 56 + 1040 128 56 + 240
22 216 56 + 3344 216 56 + 408
43 384 56 + 3344 384 56 + 752
したがって、Python 3 では dict が小さいにもかかわらず、__slots__
メモリを節約するためにインスタンスを適切にスケーリングすることがわかります__slots__
。
私のメモを完全にするために、スロットは「メンバー」と呼ばれるプロパティのようなデータ記述子を使用するため、Python 2 では 64 バイト、Python 3 では 72 バイトのクラスの名前空間のスロットごとに 1 回限りのコストがあることに注意してください。
>>> Foo.foo
<member 'foo' of 'Foo' objects>
>>> type(Foo.foo)
<class 'member_descriptor'>
>>> getsizeof(Foo.foo)
72
のデモンストレーション__slots__
:
の作成を拒否するには__dict__
、 をサブクラス化する必要がありますobject
。Python 3 ではすべてがサブクラス化object
されますが、Python 2 では明示的に記述する必要がありました。
class Base(object):
__slots__ = ()
今:
>>> b = Base()
>>> b.a = 'a'
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
b.a = 'a'
AttributeError: 'Base' object has no attribute 'a'
または、定義する別のクラスをサブクラス化します__slots__
class Child(Base):
__slots__ = ('a',)
そしていま:
c = Child()
c.a = 'a'
しかし:
>>> c.b = 'b'
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
c.b = 'b'
AttributeError: 'Child' object has no attribute 'b'
__dict__
スロット化されたオブジェクトをサブクラス化しながら作成できるようにするには、 に追加'__dict__'
するだけです__slots__
(スロットは順序付けられており、既に親クラスにあるスロットを繰り返さないことに注意してください)。
class SlottedWithDict(Child):
__slots__ = ('__dict__', 'b')
swd = SlottedWithDict()
swd.a = 'a'
swd.b = 'b'
swd.c = 'c'
と
>>> swd.__dict__
{'c': 'c'}
または、サブクラスで宣言する必要さえなく__slots__
、親からのスロットを引き続き使用しますが、の作成を制限しません__dict__
:
class NoSlots(Child): pass
ns = NoSlots()
ns.a = 'a'
ns.b = 'b'
と:
>>> ns.__dict__
{'b': 'b'}
ただし、__slots__
多重継承で問題が発生する可能性があります。
class BaseA(object):
__slots__ = ('a',)
class BaseB(object):
__slots__ = ('b',)
両方の空でないスロットを持つ親から子クラスを作成すると失敗するため:
>>> class Child(BaseA, BaseB): __slots__ = ()
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
class Child(BaseA, BaseB): __slots__ = ()
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
この問題が発生した場合は、親から削除するか、親を制御できる場合は、空のスロットを与えるか、抽象化にリファクタリングすることができます。__slots__
from abc import ABC
class AbstractA(ABC):
__slots__ = ()
class BaseA(AbstractA):
__slots__ = ('a',)
class AbstractB(ABC):
__slots__ = ()
class BaseB(AbstractB):
__slots__ = ('b',)
class Child(AbstractA, AbstractB):
__slots__ = ('a', 'b')
c = Child() # no problem!
に追加'__dict__'
し__slots__
て、動的割り当てを取得します。
class Foo(object):
__slots__ = 'bar', 'baz', '__dict__'
そしていま:
>>> foo = Foo()
>>> foo.boink = 'boink'
そのため、 '__dict__'
in スロットでは、サイズの利点の一部が失われますが、動的な割り当てがあり、期待する名前のスロットがまだあるという利点があります。
スロット化されていないオブジェクトから継承する場合、スロット化された値__slots__
を指す名前を使用すると、同じ種類のセマンティクスが得られ__slots__
ますが、他の値はインスタンスの に配置されます__dict__
。
__slots__
その場で属性を追加できるようにしたいという理由で避けることは、実際には正当な理由ではありません -これが必要な場合は、単に追加"__dict__"
してください。__slots__
その機能が必要な場合は、同様__weakref__
に明示的に追加できます。__slots__
名前付きタプルをサブクラス化するときに空のタプルに設定します。
namedtuple ビルトインは、非常に軽量 (本質的にタプルのサイズ) の不変インスタンスを作成しますが、利点を得るには、それらをサブクラス化する場合は自分で行う必要があります。
from collections import namedtuple
class MyNT(namedtuple('MyNT', 'bar baz')):
"""MyNT is an immutable and lightweight object"""
__slots__ = ()
利用方法:
>>> nt = MyNT('bar', 'baz')
>>> nt.bar
'bar'
>>> nt.baz
'baz'
また、予期しない属性を割り当てようとするとAttributeError
、 の作成が妨げられているため、 が発生し__dict__
ます。
>>> nt.quux = 'quux'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyNT' object has no attribute 'quux'
off を残すことで作成を許可できますが、タプルのサブタイプで非空を使用することはできません。__dict__
__slots__ = ()
__slots__
最大の注意点: 多重継承
空でないスロットが複数の親で同じ場合でも、一緒に使用することはできません。
class Foo(object):
__slots__ = 'foo', 'bar'
class Bar(object):
__slots__ = 'foo', 'bar' # alas, would work if empty, i.e. ()
>>> class Baz(Foo, Bar): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
親で空を使用する__slots__
と、最も柔軟性が高くなり、子が次の作成を防止または許可することを選択できるようになります(get 動的代入を追加'__dict__'
することにより、上記のセクションを参照) 。__dict__
class Foo(object): __slots__ = ()
class Bar(object): __slots__ = ()
class Baz(Foo, Bar): __slots__ = ('foo', 'bar')
b = Baz()
b.foo, b.bar = 'foo', 'bar'
スロットは必須ではありません。スロットを追加して後で削除しても、問題は発生しません。
ここで手足を踏み出す:インスタンス化することを意図していないミックスインを作成したり、抽象基本クラスを使用したりする場合、それらの親を空__slots__
にすることが、サブクラスの柔軟性の点で最善の方法のようです。
実証するために、まず、多重継承で使用したいコードでクラスを作成しましょう
class AbstractBase:
__slots__ = ()
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return f'{type(self).__name__}({repr(self.a)}, {repr(self.b)})'
予想されるスロットを継承して宣言することにより、上記を直接使用できます。
class Foo(AbstractBase):
__slots__ = 'a', 'b'
しかし、私たちはそれを気にしません。それは自明な単一継承です。おそらくノイズの多い属性を持つ、継承元の可能性のある別のクラスが必要です。
class AbstractBaseC:
__slots__ = ()
@property
def c(self):
print('getting c!')
return self._c
@c.setter
def c(self, arg):
print('setting c!')
self._c = arg
両方のベースに空でないスロットがある場合、以下を実行できませんでした。(実際、必要に応じて、AbstractBase
空でないスロット a と b を指定して、以下の宣言から除外することもできます。それらをそのままにしておくのは間違っています)。
class Concretion(AbstractBase, AbstractBaseC):
__slots__ = 'a b _c'.split()
そして今、多重継承を介して両方から機能を持ち、拒否__dict__
と__weakref__
インスタンス化を引き続き行うことができます:
>>> c = Concretion('a', 'b')
>>> c.c = c
setting c!
>>> c.c
getting c!
Concretion('a', 'b')
>>> c.d = 'd'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Concretion' object has no attribute 'd'
スロットを回避するその他のケース:
__class__
スロット レイアウトが同一でない限り、それらを持たない (そしてそれらを追加できない) 別のクラスで割り当てを実行する場合は、それらを避けてください。(誰が、なぜこれを行っているのかを知ることに非常に興味があります。)- long、tuple、または str などの可変長組み込み関数をサブクラス化し、それらに属性を追加する場合は、それらを避けてください。
- インスタンス変数のクラス属性を介してデフォルト値を提供することを主張する場合は、それらを避けてください。
__slots__
残りのドキュメント (3.7 dev ドキュメントが最新です)からさらに警告を引き出すことができるかもしれません。
他の回答に対する批判
現在の上位の回答は古い情報を引用しており、非常に手荒で、いくつかの重要な点で的を射ていません。
__slots__
「多くのオブジェクトをインスタンス化する場合にのみ使用」しないでください
私は引用します:
__slots__
「同じクラスのオブジェクトを多数 (数百、数千) インスタンス化する場合に使用します。」
たとえば、collections
モジュールからの抽象基本クラスは、インスタンス化されていません__slots__
が、宣言されています。
なんで?
ユーザーが拒否__dict__
または__weakref__
作成を希望する場合、それらは親クラスで使用可能であってはなりません。
__slots__
インターフェイスまたは mixin を作成するときの再利用性に貢献します。
多くの Python ユーザーが再利用性のために書いていないのは事実ですが、そうする場合、不必要なスペースの使用を拒否するオプションを持つことは価値があります。
__slots__
ピクルスを壊さない
スロット付きのオブジェクトをピクルすると、誤解を招くようなエラーが表示される場合がありますTypeError
。
>>> pickle.loads(pickle.dumps(f))
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
これは実際には正しくありません。このメッセージは、デフォルトである最も古いプロトコルから送信されます。-1
引数で最新のプロトコルを選択できます。Python 2.7 では2
(2.3 で導入された)、3.6 では です4
。
>>> pickle.loads(pickle.dumps(f, -1))
<__main__.Foo object at 0x1129C770>
Python 2.7 では:
>>> pickle.loads(pickle.dumps(f, 2))
<__main__.Foo object at 0x1129C770>
Python 3.6 で
>>> pickle.loads(pickle.dumps(f, 4))
<__main__.Foo object at 0x1129C770>
解決済みの問題なので、覚えておきたいと思います。
(2016 年 10 月 2 日まで) 受け入れられた回答に対する批判
最初の段落は、半分は短い説明で、半分は予測です。これが実際に質問に答える唯一の部分です
の正しい使い方は
__slots__
、オブジェクトのスペースを節約することです。いつでもオブジェクトに属性を追加できる動的辞書を持つ代わりに、作成後に追加できない静的構造があります。これにより、スロットを使用するオブジェクトごとに 1 つの dict のオーバーヘッドが節約されます
後半は希望的観測であり、的外れです。
これは便利な最適化である場合もありますが、Python インタープリターが十分に動的であり、オブジェクトに実際に追加があった場合にのみ dict を必要とする場合は、完全に不要です。
Python は実際にこれと同様のことを行い__dict__
ます。アクセスされたときにのみ作成しますが、データのないオブジェクトをたくさん作成するのはかなりばかげています。
2 番目の段落は単純化しすぎて、避けるべき実際の理由を見逃してい__slots__
ます。以下は、スロットを避ける本当の理由ではありません(実際の理由については、上記の私の回答の残りを参照してください)。
それらは、スロットを持つオブジェクトの動作を、制御フリークや静的型付けの弱虫によって悪用される可能性のある方法で変更します。
次に、Python を使用してそのひねくれた目標を達成するための他の方法について説明します__slots__
。.
3 番目の段落は、希望的観測です。一緒にすると、回答者が作成したものでさえなく、サイトの批評家の弾薬に貢献する、ほとんどが的外れなコンテンツです。
メモリ使用量の証拠
通常のオブジェクトとスロット付きオブジェクトをいくつか作成します。
>>> class Foo(object): pass
>>> class Bar(object): __slots__ = ()
100 万個のインスタンスを作成します。
>>> foos = [Foo() for f in xrange(1000000)]
>>> bars = [Bar() for b in xrange(1000000)]
で調べguppy.hpy().heap()
ます:
>>> guppy.hpy().heap()
Partition of a set of 2028259 objects. Total size = 99763360 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 49 64000000 64 64000000 64 __main__.Foo
1 169 0 16281480 16 80281480 80 list
2 1000000 49 16000000 16 96281480 97 __main__.Bar
3 12284 1 987472 1 97268952 97 str
...
通常のオブジェクトとそのオブジェクトにアクセスして、__dict__
再度検査します。
>>> for f in foos:
... f.__dict__
>>> guppy.hpy().heap()
Partition of a set of 3028258 objects. Total size = 379763480 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 33 280000000 74 280000000 74 dict of __main__.Foo
1 1000000 33 64000000 17 344000000 91 __main__.Foo
2 169 0 16281480 4 360281480 95 list
3 1000000 33 16000000 4 376281480 99 __main__.Bar
4 12284 0 987472 0 377268952 99 str
...
これは、 Unifying types and classes in Python 2.2からの Python の歴史と一致しています。
組み込み型をサブクラス化すると、インスタンスに余分なスペースが自動的に追加され、
__dict__
と__weakrefs__
. (__dict__
ただし、 は使用するまで初期化されないため、作成するインスタンスごとに空の辞書が占有するスペースについて心配する必要はありません。) この余分なスペースが必要ない場合は、"__slots__ = []
"という語句を に追加できます。あなたのクラス。
ジェイコブ・ハレンの引用:
の正しい使い方は
__slots__
、オブジェクトのスペースを節約することです。いつでもオブジェクトに属性を追加できる動的辞書を持つ代わりに、作成後に追加できない静的構造があります。[このように を使用すると__slots__
、すべてのオブジェクトに対して 1 つの dict のオーバーヘッドがなくなります。] これは便利な最適化である場合もありますが、Python インタープリターが十分に動的であり、実際に物体。残念ながら、スロットには副作用があります。それらはスロットを持つオブジェクトの動作を変更し、コントロール フリークや静的型付け弱者に悪用される可能性があります。これは悪いことです。なぜなら、コントロール フリークはメタクラスを悪用し、静的型付けのウィニーはデコレータを悪用する必要があるからです。なぜなら、Python では、何かを行う明らかな方法は 1 つしかないはずだからです。
CPython を十分に賢くしてスペースを節約できるようにすること
__slots__
は主要な仕事であり、それがおそらく (まだ) P3k の変更リストに載っていない理由です。
__slots__
同じクラスの多数(数百、数千)のオブジェクトをインスタンス化する場合に使用することをお勧めします。__slots__
メモリ最適化ツールとしてのみ存在します。
__slots__
属性の作成を制限するために使用することは強くお勧めしません。
を使用したピクルスオブジェクトは__slots__
、デフォルトの(最も古い)ピクルスプロトコルでは機能しません。それ以降のバージョンを指定する必要があります。
Pythonの他のイントロスペクション機能も悪影響を受ける可能性があります。
各 python オブジェクトには、__dict__
他のすべての属性を含む辞書である属性があります。たとえば、self.attr
python と入力すると、実際に実行されself.__dict__['attr']
ます。ご想像のとおり、辞書を使用して属性を保存すると、アクセスに余分なスペースと時間がかかります。
ただし、 を使用すると、そのクラス用に作成されたオブジェクトには属性__slots__
がありません。__dict__
代わりに、すべての属性アクセスはポインターを介して直接行われます。
したがって、本格的なクラスではなく C スタイルの構造が必要な場合__slots__
は、オブジェクトのサイズを圧縮し、属性アクセス時間を短縮するために使用できます。良い例は、属性 x と y を含む Point クラスです。ポイントがたくさんある場合は、__slots__
メモリを節約するために を使用してみてください。
スロットは、関数呼び出しを行うときに「名前付きメソッドディスパッチ」を排除するためのライブラリ呼び出しに非常に役立ちます。これは、SWIGのドキュメントに記載されています。スロットを使用して一般的に呼び出される関数の関数オーバーヘッドを削減したい高性能ライブラリの場合、はるかに高速です。
現在、これはOPの質問に直接関係していない可能性があります。これは、オブジェクトでスロット構文を使用することよりも、拡張機能の構築に関連しています。しかし、それはスロットの使用法とその背後にあるいくつかの理由の全体像を完成させるのに役立ちます。
クラス インスタンスの属性には、インスタンス、属性の名前、および属性の値の 3 つのプロパティがあります。
通常の属性アクセスでは、インスタンスはディクショナリとして機能し、属性の名前は値を検索するそのディクショナリのキーとして機能します。
インスタンス (属性) --> 値
__slots__ accessでは、属性の名前がディクショナリとして機能し、インスタンスが値を検索するディクショナリのキーとして機能します。
属性 (インスタンス) --> 値
flyweightパターンでは、属性の名前がディクショナリとして機能し、値がインスタンスを検索するそのディクショナリのキーとして機能します。
属性(値) --> インスタンス
__slot__
属性の非常に単純な例。
問題: なし__slots__
クラスに属性がない場合は__slot__
、オブジェクトに新しい属性を追加できます。
class Test:
pass
obj1=Test()
obj2=Test()
print(obj1.__dict__) #--> {}
obj1.x=12
print(obj1.__dict__) # --> {'x': 12}
obj1.y=20
print(obj1.__dict__) # --> {'x': 12, 'y': 20}
obj2.x=99
print(obj2.__dict__) # --> {'x': 99}
上記の例を見ると、obj1とobj2には独自のx属性とy属性があり、python はdict
各オブジェクト ( obj1とobj2 ) の属性も作成していることがわかります。
私のクラスTestにそのようなオブジェクトが何千もあるとしますか? 各オブジェクトに追加の属性dict
を作成すると、コードで多くのオーバーヘッド (メモリ、計算能力など) が発生します。
ソリューション: と__slots__
次の例では、クラスTestに__slots__
属性が含まれています。オブジェクトに新しい属性を追加できなくなり ( attribute を除くx
)、python はdict
もう属性を作成しません。これにより、多くのオブジェクトがある場合に重要になる可能性がある各オブジェクトのオーバーヘッドがなくなります。
class Test:
__slots__=("x")
obj1=Test()
obj2=Test()
obj1.x=12
print(obj1.x) # --> 12
obj2.x=99
print(obj2.x) # --> 99
obj1.y=28
print(obj1.y) # --> AttributeError: 'Test' object has no attribute 'y'
元の質問は、メモリだけでなく、一般的なユース ケースに関するものでした。したがって、大量のオブジェクトをインスタンス化するときにもパフォーマンスが向上することをここで言及する必要があります。たとえば、大きなドキュメントをオブジェクトに解析したり、データベースから解析したりするときに興味深いものです。
これは、スロットを使用する場合とスロットを使用しない場合の、100 万のエントリを持つオブジェクト ツリーの作成の比較です。参考までに、ツリーにプレーン dict を使用した場合のパフォーマンス (OSX 上の Py2.7.10):
********** RUN 1 **********
1.96036410332 <class 'css_tree_select.element.Element'>
3.02922606468 <class 'css_tree_select.element.ElementNoSlots'>
2.90828204155 dict
********** RUN 2 **********
1.77050495148 <class 'css_tree_select.element.Element'>
3.10655999184 <class 'css_tree_select.element.ElementNoSlots'>
2.84120798111 dict
********** RUN 3 **********
1.84069895744 <class 'css_tree_select.element.Element'>
3.21540498734 <class 'css_tree_select.element.ElementNoSlots'>
2.59615707397 dict
********** RUN 4 **********
1.75041103363 <class 'css_tree_select.element.Element'>
3.17366290092 <class 'css_tree_select.element.ElementNoSlots'>
2.70941114426 dict
テスト クラス (ident、appart from スロット):
class Element(object):
__slots__ = ['_typ', 'id', 'parent', 'childs']
def __init__(self, typ, id, parent=None):
self._typ = typ
self.id = id
self.childs = []
if parent:
self.parent = parent
parent.childs.append(self)
class ElementNoSlots(object): (same, w/o slots)
テストコード、詳細モード:
na, nb, nc = 100, 100, 100
for i in (1, 2, 3, 4):
print '*' * 10, 'RUN', i, '*' * 10
# tree with slot and no slot:
for cls in Element, ElementNoSlots:
t1 = time.time()
root = cls('root', 'root')
for i in xrange(na):
ela = cls(typ='a', id=i, parent=root)
for j in xrange(nb):
elb = cls(typ='b', id=(i, j), parent=ela)
for k in xrange(nc):
elc = cls(typ='c', id=(i, j, k), parent=elb)
to = time.time() - t1
print to, cls
del root
# ref: tree with dicts only:
t1 = time.time()
droot = {'childs': []}
for i in xrange(na):
ela = {'typ': 'a', id: i, 'childs': []}
droot['childs'].append(ela)
for j in xrange(nb):
elb = {'typ': 'b', id: (i, j), 'childs': []}
ela['childs'].append(elb)
for k in xrange(nc):
elc = {'typ': 'c', id: (i, j, k), 'childs': []}
elb['childs'].append(elc)
td = time.time() - t1
print td, 'dict'
del droot