为什么将.values()和.keys()确切地视为O(1)?

时间:2019-07-20 00:36:59

标签: python python-3.x big-o dictview

无法找到足够扎实的理由,为什么将字典功能(例如 .values() .keys())视为 O(1) (大写O表示法)。 (不确定 .items()是否也被视为O(1))

3 个答案:

答案 0 :(得分:5)

我不精通python,但是我发现了这一点:

  

Dictionary view objects

     

dict.keys()dict.values()和   dict.items()是视图对象。他们提供了关于   字典的条目,这意味着当字典更改时,   该视图反映了这些变化。

     

字典视图可以迭代生成各自的数据,   [...]

这意味着dict.keys()等不会返回新对象,而只是可以迭代字典的薄包装器。因此,获得此 view O(1)。遍历元素并非如此。

答案 1 :(得分:5)

您发现对.keys().values()(和.items())的引用为O(1)时,强调了性能,因为它与Python 2相反,函数返回列表,并需要O(N)时间从字典中复制对所有相关对象的引用。

在Python 3中由这些方法返回的视图对象上进行迭代仍将花费O(N)时间,因为无法避免访问每个项目,因为这是整个迭代的重点。 keysitems视图确实提供O(1)成员资格测试(例如(somekey, somevalue) in somedict.items()),这比在列表中搜索项目要高效得多。

答案 2 :(得分:1)

此答案仅适用于Python 3。

正如其他人所指出的,.values().items().keys()将视图返回到字典。本质上,它打开一个窗口以查看内部内容。如果要将其用作listtuple或其他对象,则需要手动对其进行迭代,从而导致O(n):

d = {i: i for i in range(100)} # Just creating a random dictionary
values = d.values() # This will get the view (O(1))
values = list(values) # Turn values to a list (O(n))
print(values[5]) # Print element 6 of the list.

关于实现的文档很少,因此字典视图的时间也很复杂(尽管我在下面做了一些源代码注释)。如果您想要项,值或键的列表,则必须为O(n),因为它需要遍历所有元素才能获得列表;但是,通过一些测试,您可以看到视图在O(1)时间内支持某些功能,例如成员检查:

import timeit 

# code snippet to be executed only once 
small_dict = "test_dict_1 = {i: i for i in range(100)}"
large_dict = "test_dict_1 = {i: i for i in range(1000000)}"

# code snippet whose execution time is to be measured 
view_code = ''' 
values_view = test_dict_1.values()
5 in values_view
'''

# timeit statement 
print(timeit.timeit(setup=small_dict, stmt=view_code, number=100000)) # 0.08737383900006535
print(timeit.timeit(setup=large_dict, stmt=view_code, number=100000)) # 0.05472081500192871

因此,视图支持O(1)中的某些操作,这就引出了问题。视图全部可以做什么?

以下是CPython特有的:

TLDR:字典视图可以支持典型的set可以执行的某些核心逻辑,例如contains,xor和...(以及将来基于源注释的逻辑)代码)。

python的优点在于它是开源的。稍加动机,您就可以深入研究the dictionary source code

如果您开始研究字典代码,您会注意到keysvalues这两个字典都已存储;但是,这对于解释字典视图的工作原理仍然无济于事。

从3950行开始,有一个很棒的评论:

/***********************************************/
/* View objects for keys(), items(), values(). */
/***********************************************/

从那里您可以开始看到视图或多或少地被视为sets,并允许进行几套O(1)操作,包括子集操作和成员资格检查。

我可以看到的操作受支持:和,或,或,

查看子集检查:

/* Return 1 if self is a subset of other, iterating over self;
   0 if not; -1 if an error occurred. */
static int
all_contained_in(PyObject *self, PyObject *other)
...

查看交叉点(4173行):

PyObject*
_PyDictView_Intersect(PyObject* self, PyObject *other)

查看不相交(第4252行):

static PyObject*
dictviews_isdisjoint(PyObject *self, PyObject *other)
{

还有很多其他信息,但这应该是一个很好的起点。如果其他人可以详细说明,我希望获得有关此主题的更多信息。随时编辑我的答案以添加,编辑,删除或改写它。