什么是字典视图对象?

时间:2012-01-21 23:48:19

标签: python view dictionary

在python 2.7中,我们得到了dictionary view methods

现在,我知道以下的优点和缺点:

  • dict.items()(和valueskeys):返回一个列表,以便您可以实际存储结果,
  • dict.iteritems()(等):返回一个生成器,因此您可以逐个迭代生成的每个值。

什么是dict.viewitems()(等)?他们有什么好处?它是如何工作的?毕竟什么是观点?

我读到视图总是反映字典中的更改。但是从性能和内存的角度来看它是如何表现的呢?有什么利弊?

5 个答案:

答案 0 :(得分:144)

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

>>> 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()。)

此示例显示视图的动态字符:键视图在给定时间点的键的副本,而是一个简单的窗口,显示你的钥匙;如果它们被改变了,那么你在窗口看到的东西也会发生变化。在某些情况下,此功能非常有用(例如,可以使用程序的多个部分中的键的视图,而不是每次需要时重新计算当前的键列表) - 注意如果修改了字典键迭代视图时,迭代器的行为方式没有明确定义,可以lead to errors

一个优点是,比如说,键只使用一小块固定数量的内存,并且需要一小段固定的处理器时间,因为没有创建密钥列表(另一方面,Python 2通常会不必要地创建一个新列表,如Rajendran T所引用的那样,它会占用内存和时间,其长度与列表)。要继续窗口类比,如果你想看到墙后面的风景,你只需在其中打开一个开口(你建立一个窗口);将密钥复制到列表中相应的是在墙上绘制景观的副本 - 复制需要时间,空间,并且不会自行更新。

总而言之,视图只是字典上的...视图(窗口),即使在字典发生变化之后也会显示字典的内容。它们提供的功能与列表的功能不同:键列表包含给定时间点的字典键的副本,而视图是动态的,获取速度要快得多,因为它确实如此不必复制任何数据(键或值)以便创建。

答案 1 :(得分:20)

正如您所提到的,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()会引发错误。

答案 2 :(得分:16)

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

来自Python 3.0 - dict methods return views - why?

  

主要原因是许多用例完全返回   超级清单是不必要和浪费的。这需要复制   整个内容(可能会或很多不会很多)。

     

如果您只想迭代密钥,然后创建新列表   没有必要。如果你确实需要它作为一个单独的列表(作为一个   然后,您可以从视图中轻松创建该列表。

答案 3 :(得分:16)

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

  1. 视图是“类似伪集”,因为它们不支持索引,因此您可以使用它们来测试成员资格并对其进行迭代(因为键是可清除且唯一的,键和项视图更像“集合”,因为它们不包含重复项。)
  2. 您可以存储它们并多次使用它们,例如列表版本。
  3. 因为它们反映了基础字典,所以字典中的任何更改都将改变视图,几乎肯定会改变迭代的顺序。因此,与列表版本不同,它们并非“稳定”。
  4. 因为它们反映了基础字典,所以它们几乎肯定是小代理对象;复制键/值/项将要求他们以某种方式观看原始字典并在发生更改时多次复制,这将是一个荒谬的实现。所以我期望很少的内存开销,但访问速度比直接到字典要慢一些。
  5. 所以我想关键的用例是如果你保持一个字典并反复迭代它的键/项/值,并在两者之间进行修改。您可以改为使用视图,将for k, v in mydict.iteritems():转换为for k, v in myview:。但是如果你只是在字典上迭代一次,我认为iter-版本仍然是可取的。

答案 4 :(得分:5)

视图允许您访问底层数据结构,而无需复制它。除了创建动态而不是创建列表之外,其中一个最有用的用法是in测试。假设您要检查某个值是否在dict中(无论是键还是值)。

选项一是使用dict.keys()创建密钥列表,这可行,但显然会消耗更多内存。如果dict非常大?这将是浪费。

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

让我们使用示例。我有一个包含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对象可以极大地提升性能,同时减少内存开销。当您需要执行类似操作的Set时,您应该使用它们。

注意:我正在使用Python 2.7