当前位置: 首页 > 面试题库 >

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

岳毅
2023-03-14
问题内容

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

第一项:

  • 从没有的类继承时,该类 __slots____dict__属性将始终可访问,因此__slots__ 子类中的定义是没有意义的。

第六项:

  • __slots__ 声明的动作仅限于定义它的类。结果,子类将具有一个,__dict__ 除非它们也定义__slots__ (该子类只能包含任何其他插槽的名称)。

在我看来,这些项目可能更好地措辞或通过代码显示,但是我一直在努力解决这个问题,但仍然感到困惑。我不明白怎么__slots__都应该被使用,而我试图让他们的工作更好地把握。
引用雅各布·哈伦(Jacob Hallen)的话:

正确的用法__slots__是节省对象空间。静态结构不允许在创建后添加对象,而不是具有允许随时向对象添加属性的动态字典。[这种使用__slots__消除了每个对象一个字典的开销。]尽管这有时是有用的优化,但是如果Python解释器足够动态,以至仅在实际添加了dict时才需要该字典,则完全没有必要。目的。

不幸的是,插槽有副作用。它们以一种可被控制怪胎和静态类型临时表滥用的方式更改具有插槽的对象的行为。这是不好的,因为控件怪胎应该滥用元类,而静态类型之间应该滥用装饰器,因为在Python中,应该只有一种明显的方法。

使CPython足够智能以处理节省的空间__slots__是一项艰巨的任务,这可能就是为什么它不在P3k更改列表中的原因(至今)。

问题:

有人可以用通俗易懂的语言向我解释子类继承时继承插槽的条件是什么?

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


问题答案:

就像其他人提到的那样,定义的唯一原因__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

由于字典本身仅占用140个字节,因此显然n声称“
32字节”对象并未考虑每个实例涉及的所有内容。您可以使用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实例的类是那些定义__slots__,加上大多数内置类型的类(实例具有dict的内置类型是可以在其实例上设置任意属性(例如函数)的那些类型)。插槽名称中的重叠不被禁止,但是它们是无用的,并且浪费了一些内存,因为插槽是继承的:

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

如您所见,您可以aAB实例上设置属性-AB本身仅定义slot b,但它继承aA。禁止重复继承的插槽:

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

但确实浪费了一点内存:

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

因此,实际上没有理由这样做。



 类似资料:
  • 我试图写一个类(SalaryEmployee)和子类(ComissionEmployee),但我没有得到正确的工资,我不能做SE1.SetSalesMount(20000)。我哪里出了问题? 这是主要的:

  • 问题内容: 我们有下课: 输出为: 超 子 问题: 什么礼物?是父级的对象,哪个孩子作为字段保留? 如果是这样,抽象类的继承如何工作?您不能创建抽象类的实例。 如果不是,重写方法在哪里保留? 我尝试使用Google,但发现的只是关于如何继承类的常见信息。 更新: 您仍在告诉我明显的事情。也许我的问题没有引起误解,但我将尝试改写一下: 当我们使用调用方法时,您说的是,我们正在访问父级的方法。但是,如

  • 本文向大家介绍JavaScript中的继承之类继承,包括了JavaScript中的继承之类继承的使用技巧和注意事项,需要的朋友参考一下 继承简介       在JS中继承是一个非常复杂的话题,比其他任何面向对象语言中的继承都复杂得多。在大多数其他面向对象语言中,继承一个类只需使用一个关键字即可。在JS中想要达到继承公用成员的目的,需要采取一系列措施。JS属于原型式继承,得益于这种灵活性,我们既可以

  • 问题内容: 在PHP / Java中,可以做到: 并且,Super类的所有公共/受保护的方法,属性,字段等都会自动成为Sub类的一部分,如有必要,可以重写这些类。 Javascript中的等效功能是什么? 问题答案: 我已经更改了现在的操作方式,我尝试避免使用构造函数和它们的属性,但是我从2010年起的旧答案仍然是最底层的。我现在更喜欢。适用于所有现代浏览器。 我应该注意,这通常比使用函数构造函数

  • 问题内容: 编译器或OS如何区分sig_atomic_t类型和普通的int类型变量,并确保操作是原子的?两者都使用的程序具有相同的汇编代码。如何特别注意使操作原子化? 问题答案: 不是原子数据类型。仅仅是允许您在信号处理程序的上下文中使用的数据类型,仅此而已。因此最好将其名称理解为“相对于信号处理而言是原子的”。 为了保证与信号处理程序之间的通信,仅需要原子数据类型的属性之一,即读取和更新将始终看

  • 问题内容: 我有一个Applet,它使用URLConnection通过HTTP连接加载图像。我正在为所有连接设置setUseCaches(true),但仍然看不到任何缓存行为。我图像的HTTP标头具有合理的缓存设置。如果您查看错误4528599,则有一个相当神秘的陈述: Java插件的当前版本(1.3.1)仅检查浏览器缓存中名称以.jar或.class结尾的文件。我被告知,对于Java Plug-