简体   繁体   English

什么是字典视图对象?

[英]What are dictionary view objects?

In python 2.7, we got the dictionary view methods available. 在python 2.7中,我们获得了可用的字典视图方法

Now, I know the pro and cons of the following: 现在,我知道以下的利弊:

  • dict.items() (and values , keys ): returns a list, so you can actually store the result, and dict.items() (和valueskeys ):返回一个列表,这样你就可以实际存储结果,和
  • dict.iteritems() (and the like): returns a generator, so you can iterate over each value generated one by one. dict.iteritems() (等):返回一个生成器,因此你可以迭代生成逐个生成的每个值。

What are dict.viewitems() (and the like) for? 什么是dict.viewitems() (等)? What are their benefits? 他们有什么好处? How does it work? 它是如何工作的? What is a view after all? 什么是观点?

I read that the view is always reflecting the changes from the dictionary. 我读到视图总是反映字典中的变化。 But how does it behave from the perf and memory point of view? 但是从性能和内存的角度来看它是如何表现的呢? What are the pro and cons? 有什么利弊?

Dictionary views are essentially what their name says: views are simply like a window on the keys and values (or items) of a dictionary. 字典视图基本上就是他们的名字所说的: 视图就像一个关于字典的键和值(或项)的窗口 Here is an excerpt from the official documentation for Python 3: 以下是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])

(The Python 2 equivalent uses dishes.viewkeys() and dishes.viewvalues() .) (Python 2等效使用dishes.viewkeys()dishes.viewvalues() 。)

This example shows the dynamic character of views : the keys view is not a copy of the keys at a given point in time, but rather a simple window that shows you the keys; 此示例显示了视图动态特性 :键视图不是给定时间点的键的副本,而是一个向您显示键的简单窗口; if they are changed, then what you see through the window does change as well. 如果它们被改变了,那么你在窗口看到的东西也会发生变化。 This feature can be useful in some circumstances (for instance, one can work with a view on the keys in multiple parts of a program instead of recalculating the current list of keys each time they are needed)—note that if the dictionary keys are modified while iterating over the view, how the iterator should behave is not well defined, which can lead to errors . 在某些情况下,此功能非常有用(例如,可以使用程序的多个部分中的键的视图,而不是每次需要时重新计算当前的键列表) - 注意如果修改了字典键迭代视图时,迭代器的行为方式没有明确定义,这可能导致错误

One advantage is that looking at, say, the keys uses only a small and fixed amount of memory and requires a small and fixed amount of processor time , as there is no creation of a list of keys (Python 2, on the other hand, often unnecessarily creates a new list, as quoted by Rajendran T, which takes memory and time in an amount proportional to the length of the list). 一个优点是,例如, 查看密钥仅使用少量固定数量的内存,并且需要少量且固定数量的处理器时间 ,因为没有创建密钥列表(另一方面,Python 2,通常不必要地创建一个新的列表,如Rajendran T引用的那样,它占用的内存和时间与列表的长度成比例)。 To continue the window analogy, if you want to see a landscape behind a wall, you simply make an opening in it (you build a window); 要继续窗口类比,如果你想看到墙后面的风景,你只需在其中打开一个开口(你建立一个窗口); copying the keys into a list would correspond to instead painting a copy of the landscape on your wall—the copy takes time, space, and does not update itself. 将密钥复制到列表中将相应于在墙上绘制景观的副本 - 复制需要时间,空间,并且不会自行更新。

To summarize, views are simply… views (windows) on your dictionary, which show the contents of the dictionary even after it changes. 总而言之,视图只是...字典上的视图(窗口),即使在更改后也会显示字典的内容。 They offer features that differ from those of lists: a list of keys contain a copy of the dictionary keys at a given point in time, while a view is dynamic and is much faster to obtain, as it does not have to copy any data (keys or values) in order to be created. 它们提供的功能与列表的功能不同:键列表包含给定时间点的字典键副本 ,而视图是动态的,获取速度更快,因为它不需要复制任何数据(键或值)以便创建。

As you mentioned dict.items() returns a copy of the dictionary's list of (key, value) pairs which is wasteful and dict.iteritems() returns an iterator over the dictionary's (key, value) pairs. 正如您所提到的, dict.items()返回字典的(键,值)对列表的副本,这是浪费的, dict.iteritems()返回字典(键,值)对上的迭代器。

Now take the following example to see the difference between an interator of dict and a view of dict 现在采用以下示例来查看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

Whereas a view simply shows you what's in the dict. 而视图只是向您显示字典中的内容。 It doesn't care if it changed: 它不关心它是否改变:

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

A view is simply a what the dictionary looks like now. 视图只是字典现在的样子。 After deleting an entry .items() would have been out-of-date and .iteritems() would have thrown an error. 删除条目后, .items()会过时,而.iteritems()会抛出错误。

The view methods return a list(not a copy of the list, compared to .keys() , .items() and .values() ), so it is more lightweight, but reflects the current contents of dictionary. 视图方法返回一个列表(不是列表的副本,与.keys() .items().values() ),因此它更轻量级,但反映了字典的当前内容。

From Python 3.0 - dict methods return views - why? Python 3.0 - dict方法返回视图 - 为什么?

The main reason is that for many use cases returning a completely detached list is unnecessary and wasteful. 主要原因是,对于许多用例而言,返回完全独立的列表是不必要和浪费的。 It would require copying the entire content (which may or many not be a lot). 它需要复制整个内容(可能或许多不是很多)。

If you simply want to iterate over the keys then creating a new list is not necessary. 如果您只想迭代密钥,则无需创建新列表。 And if you indeed need it as a separate list (as a copy) then you can easily create that list from the view. 如果您确实需要它作为单独的列表(作为副本),那么您可以从视图中轻松创建该列表。

Just from reading the docs I get this impression: 只是阅读文档,我得到了这样的印象:

  1. Views are "pseudo-set-like", in that they don't support indexing, so what you can do with them is test for membership and iterate over them (because keys are hashable and unique, the keys and items views are more "set-like" in that they don't contain duplicates). 视图是“伪设置”,因为它们不支持索引,因此您可以使用它们测试成员资格并对其进行迭代(因为键是可清除且唯一的,键和项视图更多“像集一样“因为它们不包含重复项”。
  2. You can store them and use them multiple times, like the list versions. 您可以存储它们并多次使用它们,例如列表版本。
  3. Because they reflect the underlying dictionary, any change in the dictionary will change the view, and will almost certainly change the order of iteration . 因为它们反映了基础字典,所以字典中的任何更改都将改变视图,并且几乎肯定会改变迭代的顺序 So unlike the list versions, they're not "stable". 因此与列表版本不同,它们不是“稳定”的。
  4. Because they reflect the underlying dictionary, they're almost certainly small proxy objects; 因为它们反映了基础字典,所以它们几乎肯定是小代理对象; copying the keys/values/items would require that they watch the original dictionary somehow and copy it multiple times when changes happen, which would be an absurd implementation. 复制键/值/项将要求他们以某种方式观看原始字典并在发生更改时多次复制,这将是一个荒谬的实现。 So I would expect very little memory overhead, but access to be a little slower than directly to the dictionary. 所以我期望很少的内存开销,但访问速度比直接到字典要慢一些。

So I guess the key usecase is if you're keeping a dictionary around and repeatedly iterating over its keys/items/values with modifications in between. 所以我想关键的用例是如果你保持一个字典并反复迭代它的键/项/值,并在两者之间进行修改。 You could just use a view instead, turning for k, v in mydict.iteritems(): into for k, v in myview: . 您可以只使用视图, for k, v in mydict.iteritems():转换for k, v in myview:转换for k, v in myview: . But if you're just iterating over the dictionary once, I think the iter- versions are still preferable. 但如果你只是在字典上迭代一次,我认为iter-版本仍然更可取。

Views let you access the underlaying data structure, without copying it. 通过视图,您可以访问底层数据结构,而无需复制它。 Besides being dynamic as opposed to creating a list, one of their most useful usage is in test. 除了是动态的,而不是创造一个名单,他们最有用的用途之一是in测试。 Say you want to check if a value is in the dict or not (either it be key or value). 假设您要检查某个值是否在dict中(无论是键还是值)。

Option one is to create a list of the keys using dict.keys() , this works but obviously consumes more memory. 选项一是使用dict.keys()创建一个键列表,这可以工作,但显然会消耗更多的内存。 If the dict is very large? 如果dict非常大? That would be wasteful. 那将是浪费。

With views you can iterate the actual data-structure, without intermediate list. 使用views您可以迭代实际的数据结构,而无需中间列表。

Let's use examples. 我们来看一些例子。 I've a dict with 1000 keys of random strings and digits and k is the key I want to look for 我有一个带有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?

As you can see, iterating view object gives a huge boost to performance, reducing memory overhead at the same time. 正如您所看到的,迭代view对象可以极大地提升性能,同时减少内存开销。 You should use them when you need to perform Set like operations. 当您需要执行Set like operations时,您应该使用它们。

Note : I'm running on Python 2.7 注意 :我正在运行Python 2.7

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM