1011

Pythonの目的は何ですか?__slots__特に、いつ使用したいのか、使用したくないのかについてはどうでしょうか。

4

11 に答える 11

1457

Pythonでは、これの目的は__slots__何ですか?また、これを避けるべきケースは何ですか?

TLDR:

特別な属性__slots__を使用すると、オブジェクト インスタンスが持つと予想されるインスタンス属性を明示的に指定し、期待される結果を得ることができます。

  1. より高速な属性アクセス。
  2. メモリのスペース節約。

スペースの節約は

  1. の代わりにスロットに値の参照を格納します__dict__
  2. 親クラスがそれらを拒否し、宣言した場合の拒否__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__ = []"という語句を に追加できます。あなたのクラス。

于 2015-01-21T04:46:42.340 に答える
271

ジェイコブ・ハレンの引用:

の正しい使い方は__slots__、オブジェクトのスペースを節約することです。いつでもオブジェクトに属性を追加できる動的辞書を持つ代わりに、作成後に追加できない静的構造があります。[このように を使用すると__slots__、すべてのオブジェクトに対して 1 つの dict のオーバーヘッドがなくなります。] これは便利な最適化である場合もありますが、Python インタープリターが十分に動的であり、実際に物体。

残念ながら、スロットには副作用があります。それらはスロットを持つオブジェクトの動作を変更し、コントロール フリークや静的型付け弱者に悪用される可能性があります。これは悪いことです。なぜなら、コントロール フリークはメタクラスを悪用し、静的型付けのウィニーはデコレータを悪用する必要があるからです。なぜなら、Python では、何かを行う明らかな方法は 1 つしかないはずだからです。

CPython を十分に賢くしてスペースを節約できるようにすること__slots__は主要な仕事であり、それがおそらく (まだ) P3k の変更リストに載っていない理由です。

于 2009-01-23T05:54:46.123 に答える
137

__slots__同じクラスの多数(数百、数千)のオブジェクトをインスタンス化する場合に使用することをお勧めします。__slots__メモリ最適化ツールとしてのみ存在します。

__slots__属性の作成を制限するために使用することは強くお勧めしません。

を使用したピクルスオブジェクトは__slots__、デフォルトの(最も古い)ピクルスプロトコルでは機能しません。それ以降のバージョンを指定する必要があります。

Pythonの他のイントロスペクション機能も悪影響を受ける可能性があります。

于 2009-01-23T05:50:21.173 に答える
69

各 python オブジェクトには、__dict__他のすべての属性を含む辞書である属性があります。たとえば、self.attrpython と入力すると、実際に実行されself.__dict__['attr']ます。ご想像のとおり、辞書を使用して属性を保存すると、アクセスに余分なスペースと時間がかかります。

ただし、 を使用すると、そのクラス用に作成されたオブジェクトには属性__slots__がありません。__dict__代わりに、すべての属性アクセスはポインターを介して直接行われます。

したがって、本格的なクラスではなく C スタイルの構造が必要な場合__slots__は、オブジェクトのサイズを圧縮し、属性アクセス時間を短縮するために使用できます。良い例は、属性 x と y を含む Point クラスです。ポイントがたくさんある場合は、__slots__メモリを節約するために を使用してみてください。

于 2009-01-23T13:38:21.337 に答える
14

スロットは、関数呼び出しを行うときに「名前付きメソッドディスパッチ」を排除するためのライブラリ呼び出しに非常に役立ちます。これは、SWIGのドキュメントに記載されています。スロットを使用して一般的に呼び出される関数の関数オーバーヘッドを削減したい高性能ライブラリの場合、はるかに高速です。

現在、これはOPの質問に直接関係していない可能性があります。これは、オブジェクトでスロット構文を使用することよりも、拡張機能の構築に関連しています。しかし、それはスロットの使用法とその背後にあるいくつかの理由の全体像を完成させるのに役立ちます。

于 2012-11-25T03:06:11.730 に答える
11

クラス インスタンスの属性には、インスタンス、属性の名前、および属性の値の 3 つのプロパティがあります。

通常の属性アクセスでは、インスタンスはディクショナリとして機能し、属性の名前は値を検索するそのディクショナリのキーとして機能します。

インスタンス (属性) --> 値

__slots__ accessでは、属性の名前がディクショナリとして機能し、インスタンスが値を検索するディクショナリのキーとして機能します。

属性 (インスタンス) --> 値

flyweightパターンでは、属性の名前がディクショナリとして機能し、値がインスタンスを検索するそのディクショナリのキーとして機能します。

属性(値) --> インスタンス

于 2014-06-04T22:09:43.207 に答える
9

__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}

上記の例を見ると、obj1obj2には独自のx属性とy属性があり、python はdict各オブジェクト ( obj1obj2 ) の属性も作成していることがわかります。

私のクラス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'
于 2016-11-02T09:18:07.440 に答える
1

元の質問は、メモリだけでなく、一般的なユース ケースに関するものでした。したがって、大量のオブジェクトをインスタンス化するときにもパフォーマンスが向上することをここで言及する必要があります。たとえば、大きなドキュメントをオブジェクトに解析したり、データベースから解析したりするときに興味深いものです。

これは、スロットを使用する場合とスロットを使用しない場合の、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
于 2016-01-12T18:42:31.110 に答える