在插槽上的Python数据模型参考部分中,有关于使用的注释列表__slots__。我对第一项和第六项感到完全困惑,因为它们似乎相互矛盾。
__slots__
第一项:
__dict__
第六项:
在我看来,这些项目可能更好地措辞或通过代码显示,但是我一直在努力解决这个问题,但仍然感到困惑。我不明白怎么__slots__都应该被使用,而我试图让他们的工作更好地把握。 引用雅各布·哈伦(Jacob Hallen)的话:
正确的用法__slots__是节省对象空间。静态结构不允许在创建后添加对象,而不是具有允许随时向对象添加属性的动态字典。[这种使用__slots__消除了每个对象一个字典的开销。]尽管这有时是有用的优化,但是如果Python解释器足够动态,以至仅在实际添加了dict时才需要该字典,则完全没有必要。目的。 不幸的是,插槽有副作用。它们以一种可被控制怪胎和静态类型临时表滥用的方式更改具有插槽的对象的行为。这是不好的,因为控件怪胎应该滥用元类,而静态类型之间应该滥用装饰器,因为在Python中,应该只有一种明显的方法。 使CPython足够智能以处理节省的空间__slots__是一项艰巨的任务,这可能就是为什么它不在P3k更改列表中的原因(至今)。
正确的用法__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
>>> sys.getsizeof(n.__dict__) 140
由于字典本身仅占用140个字节,因此显然n声称“ 32字节”对象并未考虑每个实例涉及的所有内容。您可以使用pympler等第三方扩展来做得更好:
n
>>> 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 >>>
如您所见,您可以a在AB实例上设置属性-AB本身仅定义slot b,但它继承a自A。禁止重复继承的插槽:
a
AB
b
A
>>> class ABRed(A): __slots__='a','b' ... >>> abr=ABRed() >>> abr.a = abr.b = 23
但确实浪费了一点内存:
>>> pympler.asizeof.asizeof(ab) 88 >>> pympler.asizeof.asizeof(abr) 96
因此,实际上没有理由这样做。