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

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

问题描述

插槽上的Python数据模型参考部分中,有一个列表有关使用__slots__的注意事项.我对第一项和第六项感到完全困惑,因为它们似乎相互矛盾.

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__应该使用的方式,我正在尝试获取一个更好地掌握它们的工作方式.

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

由于字典本身仅占用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__设置属性上具有更大的相对优势,但是在__slots__上却没有)全部,确实是 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实例的类是定义__slots__的类,以及大多数内置类型(其实例具有dict的内置类型是可以在其上设置任意属性(例如函数)的那些实例)的类.插槽名称上的重叠不是被禁止的,但是它们是无用的,并且会浪费一些内存,因为插槽是继承的:

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

如您所见,

可以在AB实例上设置属性aAB本身仅定义插槽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

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

so there's really no reason to do it.

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

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