web-dev-qa-db-ja.com

Python:サブクラスでの__slots__の継承は実際にどのように機能しますか?

スロットのPythonデータモデルリファレンスセクション__slots__の使用に関する注意事項のリストがあります。 1番目と6番目の項目は矛盾しているように見えるので、私は完全に混乱しています。

最初の項目:

  • __slots__のないクラスから継承する場合、そのクラスの__dict__属性は常にアクセス可能であるため、サブクラスの__slots__定義は無意味です。

6番目の項目:

  • __slots__宣言のアクションは、それが定義されているクラスに限定されます。その結果、サブクラスは__dict__(追加のスロットの名前のみを含む必要があります)も定義しない限り、__slots__を持ちます。

これらの項目は、コードでより適切に表現または表示できるように思われますが、私はこれに頭を悩ませようとしていて、まだ混乱しています。 __slots__がどのように機能するかを理解しています 使用されると想定されます そして、それらがどのように機能するかをよりよく理解しようとしています。

質問:

サブクラス化する際のスロットの継承の条件をわかりやすく説明してもらえますか?

(簡単なコード例は役に立ちますが、必須ではありません。)

59
jathanism

他の人が述べたように、__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

だからそれをする理由は本当にありません。

103
Alex Martelli
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

6番目のアイテム

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__を使用する必要はおそらくないでしょう。ある程度の柔軟性を犠牲にしてメモリを節約することだけを目的としています。何万ものオブジェクトがない限り、それは問題ではありません。

13
Georg Schölly

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'

do__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__の詳細については、 ここで私の答えを参照してください 。)

4
Aaron Hall

あなたがリンクした答えから:

__slots__の適切な使用法は、オブジェクトのスペースを節約することです。動的な口述をする代わりに...

__slots__のないクラスから継承する場合、そのクラスの__dict__属性は常にアクセス可能です」。したがって、独自の__slots__を追加しても、オブジェクトが__dict__を持つのを防ぐことはできません。スペースを節約できません。

__slots__が継承されないことについてのビットは、少し鈍感です。これは魔法の属性であり、他の属性のように動作しないことを忘れないでください。次に、この魔法のスロットの動作は継承されないということを読み直してください。 (これですべてです。)

2
Roger Pate

私の理解は次のとおりです。

  • クラスXには__dict__<------->クラスXがなく、そのスーパークラスにはすべて__slots__が指定されています

  • この場合、クラスの実際のスロットは、Xとそのスーパークラスの__slots__宣言の和集合で構成されます。この結合が互いに素でない場合、動作は未定義です(そしてエラーになります)

1
ilya n.