什么是Python字典视图对象?

在Python 2.7中,我们得到了可用的字典视图方法 。

现在,我知道以下的利弊:

  • dict.items() (和valueskeys ):返回一个列表,所以你可以实际存储结果
  • dict.iteritems() (等):返回一个生成器,所以你可以遍历每一个生成的值。

什么是dict.viewitems() (和类似的)? 他们有什么好处? 它是如何工作的? 什么是一个观点呢?

我读到这个观点总是反映字典的变化。 但是从性能和记忆的angular度来看它是如何performance的呢? 什么是正面和反面?

字典视图基本上是他们的名字所说的: 视图就像字典的键和值(或项目)上的一个窗口 。 以下是Python 3 官方文档的摘录:

 >>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500} >>> keys = dishes.keys() >>> values = dishes.values() >>> # view objects are dynamic and reflect dict changes >>> del dishes['eggs'] >>> keys # No eggs anymore! dict_keys(['sausage', 'bacon', 'spam']) >>> values # No eggs value (2) anymore! dict_values([1, 1, 500]) 

(Python 2等价物使用dishes.viewkeys()dishes.viewvalues() 。)

这个例子显示了视图dynamic特性 :键视图并不是给定时间点上的键的副本,而是一个显示键的简单窗口; 如果他们改变,那么你通过窗口看到的也会改变。 在某些情况下,此function可能很有用(例如,可以使用程序多个部分中的键进行查看,而不必在每次需要时重新计算当前键的列表)。

其中一个好处就是, 查看键盘只使用一小部分固定数量的内存,并且需要less量和固定的处理器时间 ,因为没有创build键列表(Python 2,另一方面,通常会不必要地创build一个新的列表,正如Rajendran T所引用的那样,记忆和时间与列表的长度成正比)。 要继续这个窗口的比喻,如果你想看到一堵墙后的景观,你只需要打开一个窗口(你build立一个窗口)。 将密钥复制到列表中将对应于在墙上绘制横向副本 – 复制需要时间,空间,并且不会自行更新。

总而言之,视图就是字典上的…视图(窗口),它甚至在字典发生变化之后也能显示字典的内容。 它们提供的function不同于列表的function:键列表包含给定时间点字典键的副本 ,而视图是dynamic的,并且获取速度更快,因为它不需要复制任何数据(键或值)以创build。

只是通过阅读文档,我得到了这样的印象:

  1. 视图是“伪集”,因为他们不支持索引,所以你可以做的是testing成员资格并迭代它们(因为密钥是可散列且唯一的,密钥和项目视图更“集合式“,因为它们不包含重复)。
  2. 您可以将它们存储并多次使用,如列表版本。
  3. 因为它们反映了底层字典,字典中的任何更改都会改变视图,并且几乎肯定会改变迭代的顺序 。 因此,与列表版本不同,它们不是“稳定的”。
  4. 因为它们反映了底层字典,所以几乎可以肯定是小代理对象; 复制键/值/项目将要求他们以某种方式观看原始字典,并在发生变化时多次复制,这是荒谬的实现。 所以我会期望很less的内存开销,但是访问速度要比直接到字典慢一点。

所以我猜想关键的用例是,如果你要保留一个字典,反复遍历它的键/项目/值,并修改它们之间的关系。 你可以使用一个视图,而不是for k, v in mydict.iteritems():转换for k, v in myview: 。 但是如果你只是迭代字典一次,我认为iter-版本仍然是可取的。

正如你所提到的, dict.items()返回字典的(键,值)对的列表的副本,这是浪费的, dict.iteritems()返回字典的(键,值)对上的迭代器。

现在以下面的例子来看看dict的interator和dict的不同之处

 >>> d = {"x":5, "y":3} >>> iter = d.iteritems() >>> del d["x"] >>> for i in iter: print i ... Traceback (most recent call last): File "<stdin>", line 1, in <module> RuntimeError: dictionary changed size during iteration 

而一个视图只是显示你的字典。 它不在乎是否改变:

 >>> d = {"x":5, "y":3} >>> v = d.viewitems() >>> v dict_items([('y', 3), ('x', 5)]) >>> del d["x"] >>> v dict_items([('y', 3)]) 

一个视图就是这个字典现在的样子。 删除条目后, .items()会过期, .iteritems()会引发错误。

视图方法返回一个列表(不是列表的副本,与.keys() .items().values() )相比,所以它更轻量,但反映了字典的当前内容。

从Python 3.0 – dict方法返回视图 – 为什么?

主要原因是对于许多用例来说,返回一个完全分离的列表是不必要和浪费的。 这将需要复制整个内容(可能或很多不是很多)。

如果你只是想遍历键,那么创build一个新的列表是没有必要的。 如果你真的需要它作为一个单独的列表(作为副本),那么你可以很容易地从视图中创build该列表。

通过视图可以访问底层的数据结构,而无需复制。 除了dynamic而不是创build一个列表之外,他们最有用的一个用法就是testing。 假设你想检查一个值是否在字典中(无论是键还是值)。

选项一是使用dict.keys()创build一个键列表,这个工作,但显然消耗更多的内存。 如果字典非常大? 那将是浪费。

通过views您可以迭代实际的数据结构,无需中间列表。

我们来使用例子。 我有一个随机string和数字1000键的字典, k是我想要查找的关键字

 large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. } >>> len(large_d) 1000 # this is one option; It creates the keys() list every time, it's here just for the example timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000) 13.748743600954867 # now let's create the list first; only then check for containment >>> list_keys = large_d.keys() >>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000) 8.874809793833492 # this saves us ~5 seconds. Great! # let's try the views now >>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000) 0.08828549011070663 # How about saving another 8.5 seconds? 

正如你所看到的,迭代view对象可以大大提高性能,同时减less内存开销。 当你需要执行像操作一样的Set时,你应该使用它们。

注意 :我在Python 2.7上运行