75

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

最初のアイテム:

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

6番目のアイテム:

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

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

質問:

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

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

4

5 に答える 5

124

他の人が述べたように、定義する唯一の理由は__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が取ると主張されているのは、各インスタンスに関係するすべてを考慮していないことです。pimplerなどのサードパーティの拡張機能を使用すると、より良い仕事をすることができます:

>>> import pympler.asizeof
>>> pympler.asizeof.asizeof(w)
96
>>> pympler.asizeof.asizeof(n)
288

これは、 によって節約されるメモリ フットプリントをより明確に示しています__slots__。この場合のような単純なオブジェクトの場合、200 バイトより少し少なく、オブジェクト全体のフットプリントのほぼ 2/3 です。最近では、ほとんどのアプリケーションにとって 1 メガバイトは多かれ少なかれそれほど重要ではないため、これは__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__するための相対的な利点はまったくありません。実際、それを取得するための小さな欠点です)。

さて、継承に関して: インスタンスが辞書なしであるためには、その継承チェーンの上のすべてのクラスも辞書なしのインスタンスを持つ必要があります。dict-less インスタンスを持つクラスは__slots__、 とほとんどの組み込み型を定義するクラスです (インスタンスに dict がある組み込み型は、関数などの任意の属性をインスタンスに設定できるクラスです)。スロット名の重複は禁止されていませんが、スロットは継承されるため、役に立たず、メモリを浪費します:

>>> class A(object): __slots__='a'
... 
>>> class AB(A): __slots__='b'
... 
>>> ab=AB()
>>> ab.a = ab.b = 23
>>> 

aご覧のとおり、ABインスタンスに属性を設定できます。ABそれ自体は slot のみを定義しますが、からbslot を継承します。継承されたスロットを繰り返すことは禁止されていません:aA

>>> class ABRed(A): __slots__='a','b'
... 
>>> abr=ABRed()
>>> abr.a = abr.b = 23

しかし、少しメモリを浪費します:

>>> pympler.asizeof.asizeof(ab)
88
>>> pympler.asizeof.asizeof(abr)
96

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

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

于 2009-11-29T19:34:53.447 に答える
8

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

1番目と6番目の項目は矛盾しているように見えるので、私は完全に混乱しています.

これらの項目は、実際には互いに矛盾しません。1 つ目は を実装しないクラスのサブクラス__slots__に関するもので、2 つ目は を実装するクラスのサブクラスに関するものです__slots__

実装しないクラスのサブクラス__slots__

私は、Python のドキュメントが (当然のことながら) 優れているとされている一方で、特に言語のあまり使用されていない機能に関しては、完璧ではないことをますます認識しています。次のようにドキュメントを変更します。

なしのクラスから継承すると、そのクラス__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__も定義しない限り、サブクラスには a が含まれます__slots__(これには、追加のスロットの名前のみが含まれている必要があります)。

まあ、それも正しくありません。__slots__宣言のアクションは、それが定義されているクラスに完全に限定されるわけではありません。たとえば、多重継承に影響を与える可能性があります。

私はそれを次のように変更します:

を定義する継承ツリー内のクラス__slots____dict__の場合、サブクラスも定義しない限りサブクラスは を持ちます__slots__(追加のスロットの名前のみを含む必要があります)。

私は実際にそれを読むように更新しました:

宣言のアクションは、__slots__それが定義されているクラスに限定されません。__slots__親で宣言されたものは、子クラスで使用できます。ただし、子サブクラスも定義しない限り、 __dict__and を取得します(追加スロットの名前のみを含む必要があります)。__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__こちらの回答を参照してください。)

于 2016-08-17T16:58:41.513 に答える
2

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

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

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

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

于 2009-11-29T19:35:17.690 に答える
1

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

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

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

于 2009-11-29T19:22:38.240 に答える