8

I've been trying to understand built-in view objects return by .items(), .values(), .keys() in Python 3 or similarly by .viewitems(), .viewvalues(), .viewkeys(). There are other threads on that subject but none (even the doc) seems to described how they work internally.

The main gain here seems to be efficienty compared to the copy of type list returned in Python 2. There are often compared to a window to the dictionnary items (like in this thread).

But what is that window and why is it more efficient ?

The only thing I can see is that the view objects seems to be set-like objects, which are generally faster for membership testing. But is this the only factor ?

Code sample

>>> example_dict = {'test':'test'}
>>> example_dict.items()
dict_items([('test', 'test')])
>>> type(example_dict.items())
<class 'dict_items'>

So, my question is regarding this dict_items class. How does that work internally?

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
scharette
  • 9,437
  • 8
  • 33
  • 67
  • Have you read this: https://stackoverflow.com/questions/8957750/what-are-python-dictionary-view-objects – Haochen Wu Nov 13 '17 at 20:52
  • I did read it, but as I said in my question it uses the window analogy which i can't get my head around in Python internal part. – scharette Nov 13 '17 at 20:55
  • @vaultah I really don't think it is a duplicate since it did not described what his _window_ represents which is exactly what I am asking. If you still think it is, can you explain what is this magic window ? – scharette Nov 13 '17 at 20:57
  • The last paragraph of the top voted answer says *"To summarize, views are simply… views (windows) on your dictionary, which show the contents of the dictionary even after it changes"*. Does this not answer your question? – vaultah Nov 13 '17 at 21:03
  • @vaultah: "window" is really vague and fuzzy. It's not really any better than "view", and the word "view" didn't get the point across either. It's not as good an explanation as it might seem from the perspective of someone who already understands the subject. – user2357112 Nov 13 '17 at 21:05
  • Well as I said, _views are simply… views (windows) on your dictionary_, what are those _windows_ ? a list ? a set ? a dictionnary ? a reference ? Maybe my question is an exact duplicate and if it is I'll be the first one to admit it, but I really don't understand the _window_ thing in the accepted answer. – scharette Nov 13 '17 at 21:08
  • @user2357112 I agree and this exactly what i tried to underlined, I added the link in my question so that people don't get confused on why I'm asking that. – scharette Nov 13 '17 at 21:13
  • @user2357112 well you didn't explain the "window" terminology either? As far as I can tell, your answer isn't much different from the answers on the linked page. Why did you feel compelled to reopen the question to post your answer (that is, in its current state)? You could post it there, after all. – vaultah Nov 13 '17 at 21:17
  • 1
    @vaultah: My answer explains what the view actually does. It doesn't use the word "window" because I didn't think the word would help explain what views actually do. – user2357112 Nov 13 '17 at 21:22

2 Answers2

7

Dict views store a reference to their parent dict, and they translate operations on the view to corresponding operations on the dict.

Iteration over a dict view is more efficient than building a list and iterating over that, because building a list takes time and memory that you don't have to spend with the view. The old way, Python would iterate over the dict's underlying storage to build a new list, and then you would iterate over the list. Iterating over a dict view uses an iterator that walks through the dict's underlying storage directly, skipping the unnecessary list step.

Dict views also support efficient containment tests and setlike intersection/difference/etc. operations, because they get to perform direct hash lookups on the underlying dict instead of iterating through a list and checking equality element by element.

If you want to see the concrete implementation used by CPython, you can take a look in the official repository, but this implementation is subject to change. It has changed, repeatedly.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 1
    I like this answer. Just a minor linguistic quibble: "... and they translate operations on the view to corresponding operations on the dict." This suggests that you can make changes to the dict through a view which is not really true, is it? – Paul Panzer Nov 13 '17 at 22:20
  • Don't want to get out of context, but what do you mean by _Dict views store a reference to their parent dict_ ? – scharette Nov 14 '17 at 02:26
  • 1
    @scharette: Imagine the dict view's `__init__` consisted of `self._secret_inaccessible_attribute = the_dict_youre_making_a_view_of`. It's like that, but in C. – user2357112 Nov 14 '17 at 02:32
  • This is exactly the kind of answer I was looking for. Thank you ! – scharette Nov 14 '17 at 12:26
  • 1
    Could you clarify "A dict view gives you an iterator" please? Calling `next` on any of the view objects will throw a `TypeError` so I'm unsure whether this was a slip in terminology or whether there is another interpretation to "iterator" in this case that is separate from my understanding of the iterator protocol? – roganjosh May 02 '20 at 10:04
  • 2
    @roganjosh: That line is referring to the iterator the dict view gives you when you iterate over it. It is not saying that the dict view itself is an iterator. – user2357112 May 02 '20 at 10:07
5

One of the main advantages is that views are dynamic:

>>> di={1:'one',2:'two',3:'three'}
>>> view=di.viewitems()
>>> view
dict_items([(1, 'one'), (2, 'two'), (3, 'three')])
>>> di[2]='new two'
>>> view
dict_items([(1, 'one'), (2, 'new two'), (3, 'three')])

Therefore you do not need to regenerate the item, key or value list (as you would with dict.items()) if the dictionary changes.

Think of the Python 2 dict.items() as a type of copy of the dict -- the way it was when the copy was made.

Think of Python 3 dict.items() or the Python 2 equivalent of dict.viewitems() as an up-to-date copy of the way the dict is now. (Same with .viewkeys(), .viewvalues() obviously.)

The Python 3.6 documents have good examples of why and when you would use one.

Value views are not set-like, since dicts can have duplicate values. Key views are set-like, and items views are set-like for dicts with hashable values.

Note: With Python 3, the view replaces what Python 2 had with .keys() .values() or .items() Some may relying on dict.keys() or dict.values() being a static representation of a dict's previous state may have a surprise.

dawg
  • 98,345
  • 23
  • 131
  • 206
  • "Therefore you do not need to regenerate the item, key or value list if the dictionary changes" - but the standard idiom is still to make a new view every time you use one, because making a view is cheap. It's rare to see anyone bother to store one. – user2357112 Nov 13 '17 at 21:11
  • @user2357112: "It's rare to see anyone bother to store one" If you look at the [documentation](https://docs.python.org/3/library/stdtypes.html?highlight=dict#dictionary-view-objects) the examples show storing the views by name. Do you have an example of it being somewhat better to regenerate rather than refer to a named object? – dawg Nov 13 '17 at 21:16
  • Most use of dict views in practice is stuff like `for key, val in d.items()` or `for v in d.values()`. I've never seen anyone bother to save a reference to the view for that. – user2357112 Nov 13 '17 at 21:22
  • Grabbing a few arbitrary Github repos (Django, Scikit-learn, and CPython) and searching them for the keyword `items`, the only place I saw anyone storing a dict view was in the dict view test cases. – user2357112 Nov 13 '17 at 21:37
  • If you do not name the view, however, the dynamic nature of the view is less than obvious. It would be rare for a dict to be changing at the same time you are iterating over the view with something like `for k, v in d.viewitems()`. Can you thing of an example where that would be true? – dawg Nov 13 '17 at 22:13
  • Occasionally people want to change the values for keys while iterating over a dict. It's still not really visible in those cases, though, because it's usually changing the value for the current key, not other keys, and the iterator won't re-fetch the value for the current key. – user2357112 Nov 13 '17 at 22:19
  • I would refer again to the documents on Python 3 dict.items(): **Iterating views while adding or deleting entries in the dictionary may raise a RuntimeError or fail to iterate over all entries.** You can change values of existing keys only, but why have that particular case dynamic? I think it is just a more elegant evolution of what use to be `dict.iteritems()` in Python 2. – dawg Nov 13 '17 at 22:46
  • Thank you for your insight guys ! – scharette Nov 14 '17 at 12:27