14

在 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

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

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

我预计至少 Python 2.7 的大小会有所不同,所以我认为测试有问题。

4

2 回答 2

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

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


这怎么可能?

__slots__因为和 key-split之间还有两个区别__dict__

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

其次,字典本身不是免费的;它有一个标准的对象头、一个计数和两个指针。这听起来可能不是很多,但是当您谈论一个只有几个属性的对象时(请注意,大多数对象只有几个属性......),dict 标头可以产生与哈希表一样大的差异。

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


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

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

想想你在哪里使用__slots__. 要么节省的钱太多以至于不使用__slots__会很荒谬,要么你真的需要挤出最后的 15%。或者,您正在构建一个 ABC 或其他您希望被谁知道的子类和子类可能需要节省的类。无论如何,在这些情况下,您获得一半的利益而没有利益__slots__,甚至三分之二的利益,仍然很少是足够的;你仍然需要使用__slots__.

真正的胜利是在不值得使用的情况下__slots__;您将免费获得小额福利。

(此外,肯定有一些程序员过度使用__slots__.

于 2014-08-03T05:08:07.757 回答
4

问题是sys.getsizeof(),它很少返回你所期望的。例如,在这种情况下,它计算对象的“大小”而不考虑其__dict__. 我建议您通过测量创建 100'000 个实例的实际内存使用情况来重试。

另请注意,Python 3.3 的行为是受 PyPy 启发的,它__slots__没有任何区别,所以我希望它在 Python 3.3 中也没有区别。据我所知,__slots__现在几乎没有任何用处。

于 2012-12-07T12:34:31.807 回答