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

Posted

技术标签:

【中文标题】Python 3.0 - dict 方法返回视图 - 为啥?【英文标题】:Python 3.0 - dict methods return views - why?Python 3.0 - dict 方法返回视图 - 为什么? 【发布时间】:2010-09-25 08:19:03 【问题描述】:

dict 方法 dict.keys(), dict.items() 和 dict.values() 返回“视图” 而不是列表。 http://docs.python.org/dev/3.0/whatsnew//3.0.html

首先,视图与迭代器有何不同?其次,这种变化有什么好处?仅仅是出于性能原因吗?

这对我来说似乎并不直观,即,我要一份东西的清单(把你所有的钥匙给我),然后我又得到了别的东西。这会让人们感到困惑吗?

【问题讨论】:

这是关于 *** 的一个很好的答案:***.com/questions/8957750/… 网址好像失效了。 【参考方案1】:

您实际上得到了一份清单。它不是内部列表的副本,而是类似于列表但仅代表内部状态的东西。

这与它在 Java(可能还有许多其他语言/环境)中实现的方式相同。

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

如果您只是想遍历键,则无需创建新列表。如果您确实需要将其作为单独的列表(作为副本),那么您可以轻松地从视图中创建该列表。

【讨论】:

【参考方案2】:

Joachim Sauer 的回答很好地解释了为什么不返回 list。但这留下了为什么这些函数不会返回迭代器的问题,就像iteritems 等在 Python 2 中所做的那样。

迭代器比容器更严格。例如,一个迭代器不允许超过一次传递;如果你尝试第二遍,你会发现它是空的。因此,容器支持elem in cont等操作,但迭代器不支持:一旦检查元素是否在迭代器“内”,迭代器就会被销毁!

另一方面,获取容器通常需要制作副本,例如从字典的键中创建一个列表。

view 对象具有两全其美的优点:它充当容器,但不会复制字典!实际上,它是一种通过链接到底层字典来工作的虚拟只读容器。我不知道它是否在标准 Python 的其他任何地方都可以看到。

编辑:

@AntonyHatchkins:它不返回生成器函数的原因是它不允许快速的in 操作。是的,in 适用于生成器函数(当您调用它们时)。也就是说,您可以这样做:

def f():
  for i in range(10):
    yield i

5 in f() # True

但是根据in的定义,如果右边是生成器,python会遍历生成器的所有n项——导致O(n)时间复杂度。您对此无能为力,因为这是任意生成器唯一有意义的行为。

另一方面,在字典视图的情况下,您可以以任何您喜欢的方式实现in,因为您对自己管理的数据了解得更多。事实上in 是使用哈希表以O(1) 复杂性实现的。你可以通过运行来检查它

>>> d = dict(zip(range(50000000), range(50000000)))
>>> 49999999 in d
True
>>> 49999999 in iter(d) # kinda how generator function would work
True
>>>

并注意到第一个 in 与第二个 in 相比有多快。

【讨论】:

然后是另一个问题,为什么不返回生成器函数。它们是多通道实体(因此也支持elem in cont 操作)并且不会占用太多内存。 @AntonyHatchkins 编辑了答案以解释原因 谢谢,从那时起我自己就学会了,但它可能对其他人也有用。【参考方案3】:

正如在相关问题中已经提到的那样,视图有 len() 方法,这是迭代器所缺少的(但列表有它)。

返回视图而不是列表的另一个好处是,至少对于键,它在 O(1) 操作中优化了成员资格测试,而不是对列表(或迭代器)进行 O(N)。

【讨论】:

以上是关于Python 3.0 - dict 方法返回视图 - 为啥?的主要内容,如果未能解决你的问题,请参考以下文章

Python 字典 update() 方法

Python字典如何根据值返回键

Python字典如何根据值返回键

Python字典dict的常用方法和函数

Python 字典 copy()方法

Python 字典(Dictionary) update()方法