Python:子类中__slots__的继承实际上如何工作? [英] Python: How does inheritance of __slots__ in subclasses actually work?

查看:235
本文介绍了Python:子类中__slots__的继承实际上如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

插槽上的Python数据模型参考部分中,列出了关于使用 __ slots __ 的说明。我对第1和第6项完全感到困惑,因为它们似乎相互矛盾。

In the Python data model reference section on slots there 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.

第一项:


  • 继承自没有
    __ slots __ 的类,该类的 __ dict __ 属性
    将始终为
    可访问,因此子类中的 __ slots __
    定义是
    无意义。

  • 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 __
    (必须只包含任何
    额外插槽的名称)。

  • 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 __ / a>,我正在努力更好地掌握它们的工作原理。

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.

问题:

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

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

推荐答案

正如其他人所提到的,定义 __ slots __ 的唯一原因是为了节省一些内存,当你有一组具有预定义属性的简单对象时,并不希望每个都有随身携带一本字典。当然,这对于您计划拥有多个实例的类来说是有意义的。

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.

节省可能不会立即明显 - 考虑......:

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

从这看起来,它似乎是带槽尺寸比无槽尺寸大!但这是一个错误,因为 sys.getsizeof 不考虑对象内容,如字典:

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

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

由于dict单独需要140个字节,显然,32字节对象 n 被指控不考虑每个实例中涉及的所有内容。您可以使用第三方扩展程序做得更好,例如 pympler

Since the dict alone takes 140 bytes, clearly the "32 bytes" object n is 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:

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

这更清楚地显示了内存占用这是由 __ slots __ 保存的:对于一个简单的对象,例如这种情况,它有点小于200字节,几乎是对象总体足迹的2/3。现在,从这些日子开始,对于大多数应用程序而言,一兆字节或多或少并不重要,这也告诉您如果你是 __ slots __ 是不值得的。一次只有几千个实例 - 然而,对于数百万个实例,它肯定会产生非常重要的差异。您还可以获得微观加速(部分原因是对于具有 __ slots __ 的小对象更好的缓存使用):

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__):

$ 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 __ 对<的更大相对优势strong>设置一个属性,但根本没有,对于获取来确实是一个很小的 dis 优势。

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 setting an attribute, but none at all, indeed a tiny disadvantage, for getting it).

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

Now, regarding inheritance: for an instance to be dict-less, all classes 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:

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

如你所见,你可以设置属性 a AB 实例 - AB 本身只定义插槽 b ,但它从 A 继承了插槽 a 。不禁止重复继承的广告位:

as you see, you can set attribute a on an AB instance -- AB itself only defines slot b, but it inherits slot a from A. Repeating the inherited slot isn't forbidden:

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

因此没有理由这样做。

这篇关于Python:子类中__slots__的继承实际上如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆