PEP 412会使__slots__多余吗?

aqu*_*tae 14 python python-3.3

在Python 3.3中实现的PEP 412引入了对属性字典的改进处理,有效地减少了类实例的内存占用. __slots__是为了相同的目的而设计的,那么使用它还有什么意义__slots__吗?

为了自己找出答案,我运行了以下测试,但结果没有多大意义:

class Slots(object):
    __slots__ = ['a', 'b', 'c', 'd', 'e']
    def __init__(self):
        self.a = 1
        self.b = 1
        self.c = 1
        self.d = 1
        self.e = 1  

class NoSlots(object):
    def __init__(self):
        self.a = 1
        self.b = 1
        self.c = 1
        self.d = 1
        self.e = 1
Run Code Online (Sandbox Code Playgroud)

Python 3.3结果:

>>> sys.getsizeof([Slots() for i in range(1000)])
Out[1]: 9024
>>> sys.getsizeof([NoSlots() for i in range(1000)])
Out[1]: 9024
Run Code Online (Sandbox Code Playgroud)

Python 2.7结果:

>>> sys.getsizeof([Slots() for i in range(1000)])
Out[1]: 4516
>>> sys.getsizeof([NoSlots() for i in range(1000)])
Out[1]: 4516
Run Code Online (Sandbox Code Playgroud)

我希望大小至少与Python 2.7不同,所以我认为测试有问题.

aba*_*ert 5

不,PEP 412并没有__slots__多余的。


首先,阿明·里戈(Armin Rigo)对您未正确评估它是正确的。您需要测量的是对象的大小,加上值,加上__dict__本身(NoSlots仅用于)和键(NoSlots仅用于)。

或者您可以按照他的建议去做:

cls = Slots if len(sys.argv) > 1 else NoSlots
def f():
    tracemalloc.start()
    objs = [cls() for _ in range(100000)]
    print(tracemalloc.get_traced_memory())
f()
Run Code Online (Sandbox Code Playgroud)

当我在OS X的64位CPython 3.4上运行此命令时,我会得到8824968for Slots25624872for NoSlots。因此,看起来一个NoSlots实例占用88个字节,而一个Slots实例占用256个字节。


这怎么可能?

因为__slots__和键分割之间仍然存在两个区别__dict__

首先,字典使用的哈希表保持在不足2 / 3rd的水平,并且它们呈指数增长并且具有最小大小,因此您将拥有一些额外的空间。通过查看注释良好的源代码,不难得出多少空间:您将拥有8个哈希存储桶,而不是5个插槽指针。

其次,字典本身不是免费的。它具有一个标准的对象标头,一个计数和两个指针。听起来似乎不多,但是当您谈论的是只有几个属性的对象(请注意,大多数对象只有几个属性……)时,dict头与哈希表的作用一样大。

当然,在您的示例中是值,因此这里涉及的唯一成本是对象本身,再加上5个插槽或8个哈希桶和dict标头,因此差异非常大。在现实生活中,__slots__很少有这么多好处。


最后,请注意,PEP 412仅声明:

基准测试表明,面向对象程序的内存使用量减少了10%到20%

想想你在哪里使用__slots__。要么节省__slots__下来的钱如此之多,以至于不使用将是荒谬的,或者您真的需要挤出最后15%的费用。或者您希望通过谁也不知道的什么,子类可能需要储蓄作为子类,你建立一个ABC或其他类。无论如何,在那种情况下,获得一半收益而没有收益__slots__,甚至三分之二的事实仍然很少是足够的。您仍然需要使用__slots__

真正的胜利是在不值得使用的情况下__slots__;您将免费获得一点好处。

(当然,肯定有一些程序员过度使用了__slots__,如果您幸运的话,这种改变可以说服他们中的一些人投入精力进行微优化,而不是无关紧要的其他事情。)

  • 您提供了“ NoSlots”和“ Slots”实例的内存大小,但是确定顺序吗?“ Slots”实例不应该比“ NoSlots”实例轻吗?这是我使用Python 3.4在Win 7 64位上获得的。 (3认同)