繁体   English   中英

PEP 412 是否使 __slots__ 变得多余?

[英]Does PEP 412 make __slots__ redundant?

在 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 的大小会有所不同,所以我假设测试有问题。

不,PEP 412不会使__slots__变得多余。


首先,Armin Rigo 是对的,您没有正确测量它。 您需要测量的是对象的大小、值、 NoSlots __dict__和键(仅适用于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 上运行它时,我得到8824968Slots25624872NoSlots 所以,看起来NoSlots实例需要 88 个字节,而Slots实例需要 256 个字节。


这怎么可能?

因为__slots__和键拆分__dict__之间仍然存在两个差异。

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

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

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


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

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

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

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

(此外,肯定有一些程序员过度使用了__slots__ ,也许这个变化可以说服他们中的一些人将他们的精力投入到微优化其他不太无关紧要的事情上,如果你幸运的话。)

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM