__slots__ 如何避免字典查找?
Posted
技术标签:
【中文标题】__slots__ 如何避免字典查找?【英文标题】:How does __slots__ avoid a dictionary lookup? 【发布时间】:2012-12-16 14:44:44 【问题描述】:我听说__slots__
通过避免字典查找使对象更快。我的困惑来自 Python 是一种动态语言。在静态语言中,我们通过进行编译时优化以将索引保存在我们运行的指令中,从而避免了对a.test
的字典查找。
现在,在 Python 中,a
可以很容易地成为另一个具有字典或一组不同属性的对象。看起来我们仍然需要进行字典查找 - 唯一的区别似乎是我们只需要一个字典来存储类,而不是每个对象都需要一个字典。
有了这个理性,
__slots__
如何避免字典查找?
槽是否可以更快地访问对象?
【问题讨论】:
@JBernardo 不,这个问题是关于它应该如何使用,而这是关于它是如何实现的(一个特定方面)。 我认为不是,您第二段中的推理似乎是正确的。您从哪里听说它避免了 dict 查找? @delnan:因为文档声明它删除了实例__dict__
;描述符被用于每个命名槽。
@MartijnPieters 那是消除字典,这与消除字典查找不同。由于 OP 的原因(我同意),仍然有一个查找,只是在类的字典中。
@delnan:我完全同意这一点,只是解释了为什么 OP 可能会混淆两者。 :-)
【参考方案1】:
__slots__
不会(显着)加快属性访问:
>>> class Foo(object):
... __slots__ = ('spam',)
... def __init__(self):
... self.spam = 'eggs'
...
>>> class Bar(object):
... def __init__(self):
... self.spam = 'eggs'
...
>>> import timeit
>>> timeit.timeit('t.spam', 'from __main__ import Foo; t=Foo()')
0.07030296325683594
>>> timeit.timeit('t.spam', 'from __main__ import Bar; t=Bar()')
0.07646608352661133
使用__slots__
的目的是节省内存;而不是在实例上使用.__dict__
映射,该类为__slots__
中命名的每个属性都具有descriptors objects,并且实例具有分配的属性无论是否它们具有实际值:
>>> class Foo(object):
... __slots__ = ('spam',)
...
>>> dir(Foo())
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'spam']
>>> Foo().spam
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: spam
>>> Foo.spam
<member 'spam' of 'Foo' objects>
>>> type(Foo.spam)
<type 'member_descriptor'>
所以python仍然需要查看Foo
实例上每个属性访问的类(以找到描述符)。任何未知属性(例如Foo.ham
)仍将导致 Python 通过 MRO 类查找该属性,其中包括字典搜索。你仍然可以为 class 分配额外的属性:
>>> Foo.ham = 'eggs'
>>> dir(Foo)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'ham', 'spam']
>>> Foo().ham
'eggs'
插槽描述符在创建类时创建,并访问分配给每个实例的内存以存储和检索对关联值的引用(跟踪实例引用计数的同一块内存和对类对象的引用)。如果没有插槽,则使用 __dict__
的描述符以相同的方式访问对 dict
对象的引用。
【讨论】:
“不显着”?在我的书中,单行更改 10% 的改进是相当显着的。当然,如果您了解__slots__
的晦涩副作用 :)
另见:http://code.activestate.com/recipes/532903-how-__slots__-are-implemented/
对于好奇的人:如果您不使用 __slots__
而是在类级别声明成员,您会看到速度 penalty 超过 5%普通的非插槽版本。【参考方案2】:
它可能会加快程序的速度,在该程序中,您实例化 很多 个相同类的对象,真正永远不会更改它们的属性,并且所有这些重复字典上的缓存未命中会带来真正的性能问题。
这只是一般情况下的一个特例,节省空间有时也可以节省时间,而缓存是限制因素。
因此,它可能不会使访问 一个 对象更快,但可能会加快访问 许多 相同类型的对象。
另见this question。
【讨论】:
以上是关于__slots__ 如何避免字典查找?的主要内容,如果未能解决你的问题,请参考以下文章