最佳答案
对我来说,内存节省似乎是因为实例上缺少 __weakref__
。
所以如果我们有:
class Spam1(object):
__slots__ = ('__dict__',)
class Spam2(object):
__slots__ = ('__dict__', '__weakref__')
class Spam3(object):
__slots__ = ('foo',)
class Eggs(object):
pass
objs = Spam1(), Spam2(), Spam3(), Eggs()
for obj in objs:
obj.foo = 'bar'
import sys
for obj in objs:
print(type(obj).__name__, sys.getsizeof(obj))
结果(在 python 3.5.2 上)是:
Spam1 48
Spam2 56
Spam3 48
Eggs 56
我们看到 Spam2
(它有一个 __weakref__
)与 Eggs
(一个传统类)大小相同。
请注意,通常情况下,这种节省将完全微不足道(并且会阻止您在启用插槽的类中使用弱引用)。通常,__slots__
的节省来自于它们首先不创建 __dict__
的事实。由于 __dict__
是使用有点稀疏的表实现的(为了帮助避免散列冲突并保持 O(1) 查找/插入/删除),因此每个空间都有相当多的空间未使用您的程序创建的字典。但是,如果您将 '__dict__'
添加到您的 __slots__
中,您将错过此优化(仍然会创建一个字典)。
为了进一步探索这一点,我们可以添加更多插槽:
class Spam3(object):
__slots__ = ('foo', 'bar')
现在如果我们重新运行,我们看到它需要:
Spam1 48
Spam2 56
Spam3 56
Eggs 56
因此每个插槽在实例 上占用 8 个字节(对我来说——可能是因为在我的系统上 8 个字节是 sizeof(pointer)
)。另请注意,__slots__
是通过创建描述符(存在于类,而不是实例)来实现的。因此,该实例(即使您可能会发现 __slots__
通过 dir(instance)
列出)实际上并没有携带 __slots__
值)- - 这是由类携带的。
这也会导致您启用插槽的类无法设置“默认”值...例如以下代码不起作用:
class Foo(object):
__slots__ = ('foo',)
foo = 'bar'
总结一下:
- 实例上的每个“插槽”占用系统上指针的大小。
- 没有
__slots__ = ('__dict__',)
在实例上创建一个__dict__
槽和一个__weakref__
槽 - 使用
__slots__ = ('__dict__',)
,创建了一个__dict__
槽,但没有在实例上创建一个__weakref__
槽。 - 在这两种情况下,
__slots__
实际上都没有放在实例 上。它存在于 类 中(即使您可能从dir(instance)
看到它)。 - 以这种方式使用
__slots__
所节省的费用可能微不足道。__slots__
的真正节省发生在您不为实例创建dict
时(因为dict
占用的存储空间大于存储空间的总和由于数据结构中的打包数据有些稀疏,因此它们的内容是必需的)。最重要的是,以这种方式使用槽也有缺点(例如,没有对您的实例的弱引用)。
关于python - 为什么 __slots__ = ('__dict__' ,) 会产生更小的实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40003067/