python 子类中 __slots__ 的继承实际上是如何工作的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1816483/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 23:07:42  来源:igfitidea点击:

How does inheritance of __slots__ in subclasses actually work?

pythoninheritancesubclassslots

提问by jathanism

In the Python data model reference section on slotsthere is a list of notes on using __slots__. I am thoroughly confused by the 1st and 6th items, because they seem to be contradicting each other.

有关插槽Python 数据模型参考部分中,有一个关于使用__slots__. 我对第 1 项和第 6 项感到非常困惑,因为它们似乎相互矛盾。

First item:

第一项:

  • When inheriting from a class without __slots__, the __dict__attribute of that class will always be accessible, so a __slots__definition in the subclass is meaningless.
  • 从没有 的类继承时,该类 __slots____dict__属性将始终是可访问的,因此__slots__在子类中的定义是没有意义的。

Sixth item:

第六项:

  • The action of a __slots__declaration is limited to the class where it is defined. As a result, subclasses will have a __dict__unless they also define __slots__(which must only contain names of any additional slots).
  • 一个的动作__slots__声明仅限于定义它的类。因此,子类将有一个,__dict__除非它们也定义了__slots__(它必须只包含任何附加插槽的名称)。

It seems to me these items could be better worded or shown through code, but I have been trying to wrap my head around this and am still coming up confused. I do understand how __slots__are supposed to be used, and I am trying to get a better grasp on how they work.

在我看来,这些项目可以用更好的措辞或通过代码显示,但我一直在努力解决这个问题,但仍然感到困惑。我不明白怎么__slots__应该被使用,而我试图让他们的工作更好地把握。

The Question:

问题:

Can someone please explain to me in plain language what the conditions are for inheritance of slots when subclassing?

有人可以用简单的语言向我解释在子类化时继承插槽的条件是什么吗?

(Simple code examples would be helpful but not necessary.)

(简单的代码示例会有所帮助,但不是必需的。)

回答by Alex Martelli

As others have mentioned, the sole reason for defining __slots__is to save some memory, when you have simple objects with a predefined set of attributes and don't want each to carry around a dictionary. This is meaningful only for classes of which you plan to have many instances, of course.

正如其他人所提到的,定义的唯一原因__slots__是节省一些内存,当您拥有具有预定义属性集的简单对象并且不希望每个对象都带有字典时。当然,这仅对您计划拥有多个实例的类有意义。

The savings may not be immediately obvious -- consider...:

节省的成本可能不会立即显现——考虑......:

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

From this, it would seem the with-slots size is largerthan the no-slots size! But that's a mistake, because sys.getsizeofdoesn't consider "object contents" such as the dictionary:

由此看来,这似乎与该型槽尺寸更大而不是无缝隙的大小!但这是一个错误,因为sys.getsizeof没有考虑诸如字典之类的“对象内容”:

>>> sys.getsizeof(n.__dict__)
140

Since the dict alone takes 140 bytes, clearly the "32 bytes" object nis alleged to take are not considering all that's involved in each instance. You can do a better job with third-party extensions such as pympler:

由于单独的 dict 需要 140 个字节,n因此据称“32 个字节”对象显然没有考虑每个实例中涉及的所有内容。您可以使用第三方扩展(例如pympler )做得更好

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

This shows much more clearly the memory footprint that's saved by __slots__: for a simple object such as this case, it's a bit less than 200 bytes, almost 2/3 of the object's overall footprint. Now, since these days a megabyte more or less doesn't really matter all that much to most applications, this also tells you that __slots__is not worth the bother if you're going to have just a few thousand instances around at a time -- however, for millions of instances, it sure does make a very important difference. You can also get a microscopic speedup (partly due to better cache use for small objects with __slots__):

这更清楚地显示了节省的内存占用__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

but this is somewhat dependent on Python version (these are the numbers I measure repeatably with 2.5; with 2.6, I see a larger relative advantage to __slots__for settingan attribute, but none at all, indeed a tiny disadvantage, for gettingit).

但是这一定程度上取决于Python版本(这些都是我用2.5重复测量号;用2.6,我看到一个较大的相对优势,__slots__设置的属性,但根本没有,确实是一个微小的DIS优势,为得到它)。

Now, regarding inheritance: for an instance to be dict-less, allclasses up its inheritance chain must also have dict-less instances. Classes with dict-less instances are those which define __slots__, plus most built-in types (built-in types whose instances have dicts are those on whose instances you can set arbitrary attributes, such as functions). Overlaps in slot names are not forbidden, but they're useless and waste some memory, since slots are inherited:

现在,关于继承:对于无字典的实例,其继承链上的所有类也必须具有无字典的实例。具有 dict-less 实例的类是那些定义__slots__,加上大多数内置类型的类(实例具有 dicts 的内置类型是那些可以在其实例上设置任意属性的类,例如函数)。插槽名称中的重叠是不被禁止的,但它们是无用的并且会浪费一些内存,因为插槽是继承的:

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

as you see, you can set attribute aon an ABinstance -- ABitself only defines slot b, but it inherits slot afrom A. Repeating the inherited slot isn't forbidden:

正如你看到的,你可以设置属性a上的AB实例-AB本身只定义插槽b,但它继承插槽aA。不禁止重复继承的插槽:

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

but does waste a little memory:

但确实浪费了一点内存:

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

so there's really no reason to do it.

所以真的没有理由这样做。

回答by Georg Sch?lly

class WithSlots(object):
    __slots__ = "a_slot"

class NoSlots(object):       # This class has __dict__
    pass

First Item

第一项

class A(NoSlots):            # even though A has __slots__, it inherits __dict__
    __slots__ = "a_slot"     # from NoSlots, therefore __slots__ has no effect

Sixth Item

第六项

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.

You probably won't need to use __slots__in the near future. It's only intended to save memory at the cost of some flexibility. Unless you have tens of thousands of objects it won't matter.

您可能__slots__在不久的将来不需要使用。它只是为了以牺牲一些灵活性为代价来节省内存。除非您有数以万计的对象,否则这无关紧要。

回答by Aaron Hall

Python: How does inheritance of __slots__in subclasses actually work?

I am thoroughly confused by the 1st and 6th items, because they seem to be contradicting each other.

Python:__slots__子类中的继承实际上是如何工作的?

我对第 1 项和第 6 项感到非常困惑,因为它们似乎相互矛盾。

Those items don't actually contradict each other. The first regards subclasses of classes that don't implement __slots__, the second regards subclasses of classes that doimplement __slots__.

这些项目实际上并不相互矛盾。第一个方面的子类没有实现类__slots__,那类第二方面的子类实现__slots__

Subclasses of classes that don't implement __slots__

未实现的类的子类 __slots__

I am increasingly aware that as great as the Python docs are (rightly) reputed to be, they are not perfect, especially regarding the less used features of the language. I would alter the docsas follows:

我越来越意识到,尽管 Python 文档(正确地)被认为是伟大的,但它们并不完美,尤其是关于该语言较少使用的功能。我会改变文档如下:

When inheriting from a class without __slots__, the __dict__attribute of that class will always be accessible, so a __slots__definition in the subclass is meaningless .

当继承自一个没有的类时,该类__slots____dict__属性将始终是可访问的,因此__slots__在子类中的定义是没有意义的

__slots__is still meaningful for such a class. It documents the expected names of attributes of the class. It also createsslots for those attributes - they will get the faster lookups and use less space. It just allows for other attributes, which will be assigned to the __dict__.

__slots__对于这样的班级还是有意义的。它记录了类属性的预期名称。它还为这些属性创建插槽 - 它们将获得更快的查找并使用更少的空间。它只允许其他属性,这些属性将分配给__dict__.

This change has been acceptedand is now in the latest documentation.

更改已被接受,现在在最新的文档中

Here's an example:

下面是一个例子:

class Foo: 
    """instances have __dict__"""

class Bar(Foo):
    __slots__ = 'foo', 'bar'

Barnot only has the slots it declares, it also has Foo's slots - which include __dict__:

Bar不仅有它声明的插槽,它还具有 Foo 的插槽 - 其中包括__dict__

>>> b = Bar()
>>> b.foo = 'foo'
>>> b.quux = 'quux'
>>> vars(b)
{'quux': 'quux'}
>>> b.foo
'foo'

Subclasses of classes that doimplement __slots__

那类的子类实现__slots__

The action of a __slots__declaration is limited to the class where it is defined. As a result, subclasses will have a __dict__unless they also define __slots__(which must only contain names of any additional slots).

一个的动作__slots__声明仅限于定义它的类。因此,子类将有一个,__dict__除非它们也定义了__slots__(它必须只包含任何附加插槽的名称)。

Well that's not quite right either. The action of a __slots__declaration is notentirely limited to the class where it is defined. They can have implications for multiple inheritance, for example.

那也不完全正确。__slots__声明的动作完全限于定义它的类。例如,它们可能对多重继承产生影响。

I would change that to:

我会将其更改为:

For classes in an inheritance tree that defines __slots__,subclasses will have a __dict__unless they also define __slots__(which must only contain names of any additional slots).

对于定义了 的继承树中的__slots__子类将有一个,__dict__除非它们也定义了__slots__(它必须只包含任何附加槽的名称)。

I have actually updated it to read:

我实际上已将其更新为:

The action of a __slots__declaration is not limited to the class where it is defined. __slots__declared in parents are available in child classes. However, child subclasses will get a __dict__and __weakref__unless they also define __slots__(which should only contain names of any additional slots).

__slots__声明的动作不限于定义它的类。__slots__在父类中声明可在子类中使用。但是,子子类将获得 a __dict__and , __weakref__除非它们也定义了__slots__(它应该只包含任何附加插槽的名称)。

Here's an example:

下面是一个例子:

class Foo:
    __slots__ = 'foo'

class Bar(Foo):
    """instances get __dict__ and __weakref__"""

And we see that a subclass of a slotted class gets to use the slots:

我们看到槽类的子类可以使用槽:

>>> b = Bar()
>>> b.foo = 'foo'
>>> b.bar = 'bar'
>>> vars(b)
{'bar': 'bar'}
>>> b.foo
'foo'

(For more on __slots__, see my answer here.)

(有关更多信息__slots__请在此处查看我的回答。)

回答by Aaron Hall

From the answer you linked:

从您链接的答案中:

The proper use of __slots__is to save space in objects. Instead of having a dynamic dict...

的正确用法__slots__是节省对象的空间。而不是有一个动态的字典......

"When inheriting from a class without __slots__, the __dict__attribute of that class will always be accessible", so adding your own __slots__cannot prevent objects from having a __dict__, and cannot save space.

“从没有 的类继承时,该类__slots____dict__属性将始终可以访问”,因此添加您自己的属性__slots__并不能阻止对象具有__dict__,并且无法节省空间。

The bit about __slots__not being inherited is a little obtuse. Remember that it's a magic attribute and doesn't behave like other attributes, then re-read that as saying this magic slots behavior isn't inherited. (That's really all there is to it.)

关于__slots__不被继承的一点有点迟钝。请记住,它是一个魔法属性,它的行为不像其他属性,然后重新阅读它,说这个魔法插槽行为不是继承的。(这就是它的全部内容。)

回答by ilya n.

My understanding is as follows:

我的理解如下:

  • class Xhas no __dict__<------->class Xand its superclasses all have __slots__specified

  • in this case, the actual slots of the class are comprised from the union of __slots__declarations for Xand its superclasses; the behavior is undefined (and will become an error) if this union is not disjoint

  • X没有__dict__<------->X,它的超类都__slots__指定了

  • 在这种情况下,类的实际槽由其__slots__声明的联合组成X;如果这个联合不是不相交的,则行为是未定义的(并且将成为错误)