Retrieve an arbitrary key from python3 dict in O(1) time

拥有回忆 提交于 2020-08-10 02:00:29

问题


I need to retrieve an arbitrary key from a python dictionary object. Suppose I have a dictionary d. What's the time complexity of the following code?

k = next(iter(d.keys()))

I get that d.key() is in O(1) time in Python 3, next() is in O(1) time. What happens to the iter()? Can this be done in O(1) time without using extra space? Thanks!


回答1:


Using iter (or other logic, such as a generator expression, declaring a generator function which delegates to the dict, using islice, etc..) are all some form of wrapper which adds the .__next__() method and some position bookkeeping to the view of the object next() operates on.

These largely work because dicts are iterable, but don't have a .__next__() method, so iter et al. are calling the __iter__ method, which returns an iterable which does have the __next__ method and is a view into the dict.

Each case is simply a wrapper around an O(1) call, so these will all operate in O(1) time once declared

https://wiki.python.org/moin/TimeComplexity


Here's a demonstration

First create a sizeable dictionary (this may take a while on slow systems)

>>> from uuid import uuid4
>>> d = {str(uuid4()):str(uuid4()) for _ in range(1000000)}

Show this can done directly from existing method

>>> next(d.__iter__()
'1273a529-d406-4076-8acc-8993f2613ad4'
>>> type(d.__iter__())
<class 'dict_keyiterator'>

Further objects

>>> n1 = iter(d)          # iter function
>>> n2 = (k for k in d)   # generator expression
>>> def y():              # generator delegation
...   yield from d
...
>>> import itertools
>>> i = itertools.islice(d, 1)  # slice one entry from iterable
>>> type(n1)
<class 'dict_keyiterator'>
>>> type(n2)
<class 'generator'>
>>> type(y())
<class 'generator'>
>>> type(i)
<class 'itertools.islice'>

Each of these can be used to read the first key

>>> next(n1)
'1273a529-d406-4076-8acc-8993f2613ad4'
>>> next(n2)
'1273a529-d406-4076-8acc-8993f2613ad4'
>>> next(y())
'1273a529-d406-4076-8acc-8993f2613ad4'
>>> next(i)
'1273a529-d406-4076-8acc-8993f2613ad4'

Proof that these have all supplied a next method

>>> dir(d)
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
>>> "__next__" in dir(d)
False
>>> "__next__" in dir(n1)
True
>>> "__next__" in dir(n2)
True
>>> "__next__" in dir(y())
True
>>> "__next__" in dir(i)
True

Finally, these can also be efficiently called in a loop until some length is reached or sliced by islice from itertools if the first N values are wanted (rather than just the first from next()), but will incur some further overhead when formed into a list or such

>>> import itertools
>>> list(itertools.islice(d, 5))
['1273a529-d406-4076-8acc-8993f2613ad4', 'a920460d-a193-455c-979c-a91fd700f927', 'aeccb371-43d1-4690-8aaa-d6de0cbe3801', '9aaf2a96-9ef4-4610-8723-8401008e190a', 'e4b450aa-50a2-409a-a5b2-ab88285eb770']
>>> list(itertools.islice(y(), 5))
['1273a529-d406-4076-8acc-8993f2613ad4', 'a920460d-a193-455c-979c-a91fd700f927', 'aeccb371-43d1-4690-8aaa-d6de0cbe3801', '9aaf2a96-9ef4-4610-8723-8401008e190a', 'e4b450aa-50a2-409a-a5b2-ab88285eb770']
>>> list(itertools.islice(n1, 5))
['1273a529-d406-4076-8acc-8993f2613ad4', 'a920460d-a193-455c-979c-a91fd700f927', 'aeccb371-43d1-4690-8aaa-d6de0cbe3801', '9aaf2a96-9ef4-4610-8723-8401008e190a', 'e4b450aa-50a2-409a-a5b2-ab88285eb770']
>>> list(itertools.islice(n2, 5))
['1273a529-d406-4076-8acc-8993f2613ad4', 'a920460d-a193-455c-979c-a91fd700f927', 'aeccb371-43d1-4690-8aaa-d6de0cbe3801', '9aaf2a96-9ef4-4610-8723-8401008e190a', 'e4b450aa-50a2-409a-a5b2-ab88285eb770']

See also Python iter() time complexity? (first comment on your answer by snatchysquid)



来源:https://stackoverflow.com/questions/62177595/retrieve-an-arbitrary-key-from-python3-dict-in-o1-time

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!