在Python 2.7,词典有两对和一viewkeysiterkeys法法(和类似的价值观和对项目),提供两种不同的方法iterate懒洋洋地在钥匙字典。酒店提供酒店委托viewkeys法学院iterkeys特征,有效地与iter(d.viewkeys())到d.iterkeys()等效。此外,一个方便的对象返回的viewkeys集样的特点。因此,一个有过强的原因是viewkeysiterkeys青睐。
其他的什么方向?除了兼容早期的版本有任何Python的方式),这将是优选的在iterkeysviewkeys?没有什么是失去了通过使用viewkeys总是准时吗?
- "除了与早期版本的python兼容之外",这是一个你几乎不能称之为无关的东西。否则,所有人都会使用python3.2
- @瓦特克不是不相干的,但很明显。
- 但是,除此之外,iterkeys()并不比viewkeys()有优势。
- @瓦特克谢天谢地,他没有说这无关紧要,那么!
字典视图像字典一样更新,而迭代器不一定这样做。
这意味着,如果您使用该视图,更改字典,然后再次使用该视图,则该视图将发生更改以反映字典的新状态。
They provide a dynamic view on the dictionary’s entries, which means that when the dictionary changes, the view reflects these changes.
Source
例子:
1 2 3 4 5 6 7 8 9
| >>> test = {1: 2, 3: 4}
>>> a = test.iterkeys()
>>> b = test.viewkeys()
>>> del test[1]
>>> test[5] = 6
>>> list(a)
[3, 5]
>>> b
dict_keys([3, 5]) |
对大小进行更改时,将引发异常:
1 2 3 4 5 6 7 8 9 10
| >>> test = {1: 2, 3: 4}
>>> a = test.iterkeys()
>>> b = test.viewkeys()
>>> test[5] = 6
>>> list(a)
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
>>> b
dict_keys([1, 3, 5]) |
同样值得注意的是,您只能对KeyIterator迭代一次:
1 2 3 4 5 6 7 8 9 10 11
| >>> test = {1: 2, 3: 4}
>>> a = test.iterkeys()
>>> list(a)
[1, 3]
>>> list(a)
[]
>>> b = test.viewkeys()
>>> b
dict_keys([1, 3])
>>> b
dict_keys([1, 3]) |
- 这是完全正确的,但实际上并没有解决这个问题——运营商知道viewkeys做了什么,并专门询问假装iterkeys不存在是否存在任何缺点。
- @LVC解释了这些差异,这可以被认为是iterkeys()的积极因素-例如,如果您不希望它得到更新,使用iterkeys()可能更合适(尽管可以使用viewkeys()创建相同的功能)。我不是说这是一个强有力的理由,但这几乎是唯一的理由。
- @拉尔斯曼有人会说,这比在你不想更新的版本上默默地工作要好,但我同意这是一种延伸。是的,简短的回答是没有理由使用iterkeys(),这就是为什么它在python3中消失的原因。不过,我想我应该解释一下这些差异,希望它能让区别变得清晰。
- @我很感激你的回答,尽管这似乎不是什么优势。我想不起来有没有遇到过在迭代或想这样做的时候修改字典而引起的runtimeerror。对实际用例有什么想法吗?
- @迈克尔·巴伯从来没有。一生成迭代器就不使用它是非常罕见的。我只是在谈论可能性,这是关于你能得到的所有差异。正如其他人所说,使用iterkeys()和friends的唯一真正原因是当您想要向后兼容(而不是向后兼容)时,将它们丢弃。
- 重要的是要认识到迭代器不会为字典中的任何更改而引发RuntimeError;只会为大小更改而引发。迭代器创建和迭代之间的del test[3]; test[5] = 6既不会使它混乱,也不会使test[3] = 91混乱。所以很危险。
- @克里斯摩根,我真的以为我已经举了一个例子,更新,干杯。这使我早先的观点,不工作在一个更新的版本无效,所以忽略这一点。
- 注意:2.7的文档页面说"在添加或删除字典中的条目时迭代视图可能会导致RuntimeError或无法对所有条目进行迭代"。我这样读是为了确保传递dictView是安全的,并在稍后(进行更改后)开始迭代,但一旦开始迭代,字典就不能安全地成为GED。如果您不知道这个警告,您可能会认为View方法在迭代字典时提供了一种修改字典的方法。不安全(在2.7中)。相反,在迭代时列出"要删除的键",然后稍后删除这些键
功能方面,正如您所观察到的,视图更好。从兼容性角度看,它们更糟。
一些性能指标取自64位Ubuntu计算机上的python 2.7.2:
1
| >>> from timeit import timeit |
处理空字典:
1 2 3 4 5 6 7 8 9
| >>> emptydict = {}
>>> timeit(lambda: emptydict.viewkeys())
0.24384498596191406
>>> timeit(lambda: list(emptydict.viewkeys()))
0.4636681079864502
>>> timeit(lambda: emptydict.iterkeys())
0.23939013481140137
>>> timeit(lambda: list(emptydict.iterkeys()))
1.0098130702972412 |
构建视图的成本稍微高一些,但是使用视图的速度要比迭代器快得多(比迭代器快一倍多)。
处理千元素字典:
1 2 3 4 5 6 7 8 9
| >>> fulldict = {i: i for i in xrange(1000)}
>>> timeit(lambda: fulldict.viewkeys())
0.24295306205749512
>>> timeit(lambda: list(fulldict.viewkeys()))
13.447425842285156
>>> timeit(lambda: fulldict.iterkeys())
0.23759889602661133
>>> timeit(lambda: list(fulldict.iterkeys()))
15.45390510559082 |
同样的结果,虽然不那么明显;构建视图的成本要稍微高一些,但是使用它肯定要快一些(快15%)。
为了与list(dict.viewkeys())和list(dict.iterkeys())进行公平比较,dict.keys()明显更快:
1 2 3 4
| >>> timeit(lambda: emptydict.keys())
0.2385849952697754
>>> timeit(lambda: fulldict.keys())
7.842105150222778 |
总结:这是一个权衡;更好的功能(您很少使用)和性能(这只会非常不重要,足以让您担心&mdash;如果您关心这些性能问题,您可能已经处于需要与numpy/scipy一起工作的区域),而不是更好的兼容性和肌肉记忆使用。
就个人而言,除非已经依赖于仅2.7的功能,或者除非我完全控制运行时环境,否则我将避免在python 2代码中使用字典视图。即使在这些情况下,我的手指仍然想用iter而不是view来打字,所以我就让他们来了!
- 我们中的一些人生活在曲线的前面,我知道我默认的只是items()和朋友,因为他们经常使用python 3.x。我回到python 2.x时必须小心。老实说,虽然3.x支持是一个混合包,但是2.7.3支持在这一点上几乎是通用的,不管是给予还是接受奇数的支持。
- @LattyWare:我没有这个问题,因为我还没有在任何项目中使用Python3(仍然依赖于Python2,比如Django)。
- 我们都被我们使用的东西所束缚。不过,我的负责人Django已经接近完整的3.x兼容性。哦,我忘了说,+1用于全面的速度分析。
- @拉蒂瓦:是的,他们的调查作为Django1.5的一部分似乎进展顺利。期待能够普遍使用python 3(当然还有其他的拖累)。
- 我的手指同意你的想法,想要输入iter,这正是我提出问题的原因!
不,与viewkeys相比,iterkeys没有优势,这与keys没有优势。iterkeys只是为了向后兼容。实际上,在python3中,viewkeys是唯一仍然存在的行为,它已经被重命名为keys—viewkeys方法实际上是python3行为的一个反向端口。
- 这并不能解释任何不同之处。
- @SystemParadox这个问题明确地没有问到底有什么区别——OP特别问使用iter*是否比view*有优势,并且已经列出了另一个方向的优势。
正如名称(和文档)所示,viewkeys()、viewvalues()和viewitems()方法返回字典中当前元素的视图,这意味着如果字典更改,视图也会更改;视图是懒惰的。在一般情况下,键视图的设置是类似的,并且只有当值是可散列的时,项视图的设置才是类似的。
在什么情况下,最好使用标准方法keys()、values()和items()?您提到了一个非常重要的问题:向后兼容性。此外,当您需要一个包含所有键、值或项的简单列表(而不是像集合,而不是迭代器)时,当您需要在不修改原始字典的情况下修改返回的列表时,以及当您需要一个字典的键、值或项的快照时,而不需要对字典进行任何后面的修改。
那么,iterkeys()、itervalues()和iteritems()呢?当您需要字典内容的一次性、恒定空间、延迟迭代器快照时(通过RuntimeError进行迭代),它们是一个合适的替代方法,可以告诉您字典是否在迭代时被修改过,而且它们对于向后兼容性非常重要。
- 恐怕不是——'(字典视图)是懒惰的序列'
- @拿铁咖啡谢谢,修好了。
- 次要说明:只有当值是可哈希值时,才会设置项视图。
- 谢谢。这是唯一能解释这些差异的答案。