什么是字典视图对象?

e-s*_*tis 149 python dictionary view

在python 2.7中,我们获得了可用的字典视图方法.

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

  • dict.items()(和values,keys):返回一个列表,这样你就可以实际存储的结果,
  • dict.iteritems() (等等):返回一个生成器,这样你就可以迭代生成逐个生成的每个值.

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

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

Eri*_*got 150

字典视图基本上就是他们的名字所说的:视图就像一个关于字典的键和值(或项)的窗口.以下是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])
Run Code Online (Sandbox Code Playgroud)

(Python 2等效使用dishes.viewkeys()dishes.viewvalues().)

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

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

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

  • +1.好吧,这与直接访问内部密钥列表有什么不同?那更快,更慢吗?内存效率更高?受限制?如果您可以阅读和编辑它,则感觉与对此列表的引用完全相同. (5认同)
  • 我认为值得指出的是,这篇文章中的示例代码来自python3,而不是我在python2.7中获得的代码. (4认同)
  • 谢谢.问题是视图*是你对"内部密钥列表"的访问(请注意,这个"密钥列表"不是Python列表,但是恰好是一个视图).视图比Python 2的键(或值或项)列表更有内存效率,因为它们不会复制任何内容; 它们确实像"对键列表的引用"(注意"对列表的引用"实际上简称为列表,在Python中,因为列表是可变对象).另请注意,您无法直接编辑视图:相反,您仍然可以编辑字典,视图会立即反映您的更改. (2认同)
  • 好吧,我还不清楚它的实现,但到目前为止它是最好的答案. (2认同)
  • 谢谢。事实上,这个答案主要是关于视图的*语义*。我没有关于它们在 CPython 中实现的信息,但我猜想视图基本上是指向正确结构(键和/或值)的指针,并且结构是字典对象本身的一部分。 (2认同)

Mar*_*cny 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
Run Code Online (Sandbox Code Playgroud)

而视图只是向您显示字典中的内容.它不关心它是否改变:

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

视图只是字典现在的样子.删除条目后,该条目.items()已经过时,并且.iteritems()会抛出错误.


Raj*_*n T 16

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

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

主要原因是,对于许多用例而言,返回完全独立的列表是不必要和浪费的.它需要复制整个内容(可能或许多不是很多).

如果您只想迭代密钥,则无需创建新列表.如果您确实需要它作为单独的列表(作为副本),那么您可以从视图中轻松创建该列表.

  • 视图方法返回视图对象,这些视图对象不符合列表界面. (4认同)
  • +1但仍然不清楚视图是什么以及它是如何工作的. (2认同)

Ben*_*Ben 16

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

  1. 视图是"伪设置的",因为它们不支持索引,因此您可以使用它们来测试成员资格并对它们进行迭代(因为键是可清除且唯一的,键和项视图更多"像集一样"因为它们不包含重复项".
  2. 您可以存储它们并多次使用它们,例如列表版本.
  3. 因为它们反映了基础字典,所以字典中的任何更改都将改变视图,并且几乎肯定会改变迭代的顺序.因此与列表版本不同,它们不是"稳定"的.
  4. 因为它们反映了基础字典,所以它们几乎肯定是小代理对象; 复制键/值/项将要求他们以某种方式观看原始字典并在发生更改时多次复制,这将是一个荒谬的实现.所以我期望很少的内存开销,但访问速度比直接到字典要慢一些.

所以我想关键的用例是如果你保持一个字典并反复迭代它的键/项/值,并在两者之间进行修改.你可以只使用一个视图代替,把for k, v in mydict.iteritems():for k, v in myview:.但如果你只是在字典上迭代一次,我认为iter-版本仍然更可取.

  • +1用于分析我们获得的少数信息的利弊. (2认同)

Che*_* A. 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?
Run Code Online (Sandbox Code Playgroud)

如您所见,迭代view对象极大地提高了性能,同时减少了内存开销。需要执行Set类似操作时,应使用它们。

注意:我在Python 2.7上运行