スロットのPythonデータモデルリファレンスセクション__slots__
の使用に関する注意事項のリストがあります。 1番目と6番目の項目は矛盾しているように見えるので、私は完全に混乱しています。
最初の項目:
__slots__
のないクラスから継承する場合、そのクラスの__dict__
属性は常にアクセス可能であるため、サブクラスの__slots__
定義は無意味です。6番目の項目:
__slots__
宣言のアクションは、それが定義されているクラスに限定されます。その結果、サブクラスは__dict__
(追加のスロットの名前のみを含む必要があります)も定義しない限り、__slots__
を持ちます。これらの項目は、コードでより適切に表現または表示できるように思われますが、私はこれに頭を悩ませようとしていて、まだ混乱しています。 __slots__
がどのように機能するかを理解しています 使用されると想定されます そして、それらがどのように機能するかをよりよく理解しようとしています。
質問:
サブクラス化する際のスロットの継承の条件をわかりやすく説明してもらえますか?
(簡単なコード例は役に立ちますが、必須ではありません。)
他の人が述べたように、__slots__
を定義する唯一の理由は、事前定義された属性のセットを持つ単純なオブジェクトがあり、それぞれが辞書を持ち歩きたくない場合に、メモリを節約することです。もちろん、これは、多くのインスタンスを持つことを計画しているクラスに対してのみ意味があります。
節約はすぐには明らかではないかもしれません-考慮してください...:
>>> class NoSlots(object): pass
...
>>> n = NoSlots()
>>> class WithSlots(object): __slots__ = 'a', 'b', 'c'
...
>>> w = WithSlots()
>>> n.a = n.b = n.c = 23
>>> w.a = w.b = w.c = 23
>>> sys.getsizeof(n)
32
>>> sys.getsizeof(w)
36
このことから、スロットありのサイズはスロットなしのサイズよりも大きいように見えます!しかし、それは間違いです。sys.getsizeof
は、辞書などの「オブジェクトの内容」を考慮しないためです。
>>> sys.getsizeof(n.__dict__)
140
Dictだけで140バイトかかるため、明らかに「32バイト」オブジェクトn
が取ると主張されているのは、各インスタンスに関係するすべてを考慮していないことです。 pympler :などのサードパーティの拡張機能を使用すると、より良い仕事をすることができます。
>>> import pympler.asizeof
>>> pympler.asizeof.asizeof(w)
96
>>> pympler.asizeof.asizeof(n)
288
これは、__slots__
によって節約されたメモリフットプリントをはるかに明確に示しています。この場合のような単純なオブジェクトの場合、200バイトより少し少なく、オブジェクト全体のフットプリントのほぼ2/3です。さて、最近ではメガバイトはほとんどのアプリケーションにとってそれほど重要ではないので、これは、数千のインスタンスが存在する場合、__slots__
は気にする価値がないことも示しています。一度に-しかし、何百万ものインスタンスにとって、それは確かに非常に重要な違いをもたらします。また、微視的なスピードアップを得ることができます(__slots__
を使用した小さなオブジェクトのキャッシュ使用が改善されたこともあります)。
$ python -mtimeit -s'class S(object): __slots__="x","y"' -s's=S(); s.x=s.y=23' 's.x'
10000000 loops, best of 3: 0.37 usec per loop
$ python -mtimeit -s'class S(object): pass' -s's=S(); s.x=s.y=23' 's.x'
1000000 loops, best of 3: 0.604 usec per loop
$ python -mtimeit -s'class S(object): __slots__="x","y"' -s's=S(); s.x=s.y=23' 's.x=45'
1000000 loops, best of 3: 0.28 usec per loop
$ python -mtimeit -s'class S(object): pass' -s's=S(); s.x=s.y=23' 's.x=45'
1000000 loops, best of 3: 0.332 usec per loop
ただし、これはPythonバージョンに多少依存します(これらは2.5で繰り返し測定する数値です。2.6では、の__slots__
よりも相対的な利点が大きくなります。設定属性ですが、まったくありません。実際、取得それのための小さな dis 利点です。
さて、継承に関して:インスタンスがdict-lessであるためには、その継承チェーンの上の all クラスもdict-lessインスタンスを持っている必要があります。ディクショナリのないインスタンスを持つクラスは、__slots__
を定義するクラスと、ほとんどの組み込み型(インスタンスにディクショナリがある組み込み型は、インスタンスに関数などの任意の属性を設定できるクラス)です。スロット名の重複は禁止されていませんが、スロットが継承されるため、それらは役に立たず、メモリを浪費します。
>>> class A(object): __slots__='a'
...
>>> class AB(A): __slots__='b'
...
>>> ab=AB()
>>> ab.a = ab.b = 23
>>>
ご覧のとおり、a
インスタンスに属性AB
を設定できます-AB
自体はスロットb
のみを定義しますが、スロットa
from A
。継承されたスロットを繰り返すことは禁止されていません:
>>> class ABRed(A): __slots__='a','b'
...
>>> abr=ABRed()
>>> abr.a = abr.b = 23
しかし、少しのメモリを浪費します:
>>> pympler.asizeof.asizeof(ab)
88
>>> pympler.asizeof.asizeof(abr)
96
だからそれをする理由は本当にありません。
class WithSlots(object):
__slots__ = "a_slot"
class NoSlots(object): # This class has __dict__
pass
class A(NoSlots): # even though A has __slots__, it inherits __dict__
__slots__ = "a_slot" # from NoSlots, therefore __slots__ has no effect
class B(WithSlots): # This class has no __dict__
__slots__ = "some_slot"
class C(WithSlots): # This class has __dict__, because it doesn't
pass # specify __slots__ even though the superclass does.
近い将来、__slots__
を使用する必要はおそらくないでしょう。ある程度の柔軟性を犠牲にしてメモリを節約することだけを目的としています。何万ものオブジェクトがない限り、それは問題ではありません。
Python:サブクラスでの
__slots__
の継承は実際にどのように機能しますか?1番目と6番目の項目は矛盾しているように見えるので、私は完全に混乱しています。
これらの項目は実際には互いに矛盾していません。 1つ目は、__slots__
を実装しないクラスのサブクラスに関するもので、2つ目は、__slots__
を実装するdoするクラスのサブクラスに関するものです。
__slots__
を実装しないクラスのサブクラスPython docsは(正しく)評判が高いのと同じくらい素晴らしいと言われていますが、特に言語のあまり使用されていない機能に関しては、完全ではないことにますます気づいています。 docs は次のようになります:
__slots__
のないクラスから継承する場合、そのクラスの__dict__
属性は常にアクセス可能です。、したがって、サブクラスの。__slots__
定義は無意味です
__slots__
は、そのようなクラスにとっても意味があります。クラスの属性の予想される名前を文書化します。また、これらの属性のスロットを作成します-ルックアップが高速になり、使用するスペースが少なくなります。 __dict__
に割り当てられる他の属性を許可するだけです。
この 変更は承認されました そして現在 最新のドキュメント にあります。
次に例を示します。
class Foo:
"""instances have __dict__"""
class Bar(Foo):
__slots__ = 'foo', 'bar'
Bar
には、宣言するスロットがあるだけでなく、Fooのスロットもあります。これには__dict__
が含まれます。
>>> b = Bar()
>>> b.foo = 'foo'
>>> b.quux = 'quux'
>>> vars(b)
{'quux': 'quux'}
>>> b.foo
'foo'
__slots__
を実装するクラスのサブクラス
__slots__
宣言のアクションは、それが定義されているクラスに限定されます。その結果、サブクラスは__dict__
(追加のスロットの名前のみを含む必要があります)も定義しない限り、__slots__
を持ちます。
まあ、それも正しくありません。 __slots__
宣言のアクションは、それが定義されているクラスに完全に限定されているわけではありません。たとえば、多重継承に影響を与える可能性があります。
私はそれを次のように変更します:
__slots__
を定義する継承ツリー内のクラスの場合、サブクラスは、__dict__
も定義しない限り、__slots__
を持ちます。 (追加のスロットの名前のみを含める必要があります)。
私は実際にそれを読んで更新しました:
__slots__
宣言のアクションは、それが定義されているクラスに限定されません。親で宣言された__slots__
は、子クラスで使用できます。ただし、子サブクラスは、__dict__
(追加のスロットの名前のみを含む必要があります)も定義しない限り、__weakref__
および__slots__
を取得します。
次に例を示します。
class Foo:
__slots__ = 'foo'
class Bar(Foo):
"""instances get __dict__ and __weakref__"""
そして、スロットクラスのサブクラスがスロットを使用できるようになることがわかります。
>>> b = Bar()
>>> b.foo = 'foo'
>>> b.bar = 'bar'
>>> vars(b)
{'bar': 'bar'}
>>> b.foo
'foo'
(__slots__
の詳細については、 ここで私の答えを参照してください 。)
あなたがリンクした答えから:
__slots__
の適切な使用法は、オブジェクトのスペースを節約することです。動的な口述をする代わりに...
「__slots__
のないクラスから継承する場合、そのクラスの__dict__
属性は常にアクセス可能です」。したがって、独自の__slots__
を追加しても、オブジェクトが__dict__
を持つのを防ぐことはできません。スペースを節約できません。
__slots__
が継承されないことについてのビットは、少し鈍感です。これは魔法の属性であり、他の属性のように動作しないことを忘れないでください。次に、この魔法のスロットの動作は継承されないということを読み直してください。 (これですべてです。)
私の理解は次のとおりです。
クラスX
には__dict__
<------->
クラスX
がなく、そのスーパークラスにはすべて__slots__
が指定されています
この場合、クラスの実際のスロットは、X
とそのスーパークラスの__slots__
宣言の和集合で構成されます。この結合が互いに素でない場合、動作は未定義です(そしてエラーになります)