11

I know that you can cast dict_items into a list to allow item indexing. But I do not know why this operation is not allowed directly. Is it because dict_items objects are generators? If so, when I'm seeing

>>> {"foo": "bar", "baz": "qux"}.items()
dict_items([('foo', 'bar'), ('baz', 'qux')]) 

is Python evaluating my generator when repr is called?

wim
  • 338,267
  • 99
  • 616
  • 750
ohe
  • 3,461
  • 3
  • 26
  • 50
  • 2
    Note that you're not *casting* it to a `list`; you're creating a new list from it. Whether or not this is efficient depends on the Python implementation. – Jonathon Reinhart Oct 19 '18 at 21:56
  • There is no built-in named `dict_items`. I assume you mean the `dict` method `items()`. – martineau Oct 19 '18 at 21:59
  • 3
    @martineau: `type({}.items()).__name__` -> `'dict_items'` – Mad Physicist Oct 19 '18 at 22:00
  • 1
    I think it's because the object of type `dict_items` that the `dict.items()` method returns is a [dictionary view object](https://docs.python.org/3/library/stdtypes.html#dict-views) which are dynamic and reflect changes that may be occurring to the dictionary on-the-fly—so indexing something like that which might change at any moment doesn't make too much sense. If you convert it to a list, the contents of that list would be changed by updates to the dictionary it came from. – martineau Oct 19 '18 at 22:39
  • Related: [Accessing dictionary items by position in Python 3.6+ efficiently](https://stackoverflow.com/questions/52507860/accessing-dictionary-items-by-position-in-python-3-6-efficiently), specifically [this answer](https://stackoverflow.com/a/52507957/9209546). – jpp Oct 19 '18 at 23:37

1 Answers1

14

dict_items do not support indexing because these objects are intended to be set-like, and sets do not support indexing.

They do quack like sets in other ways:

>>> d1 = {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
>>> d2 = {'k2': 'v2', 'k3': 'not v3'}
>>> d1.items() & d2.items()
{('k2', 'v2')}
>>> d1.items() | d2.items()
{('k1', 'v1'), ('k2', 'v2'), ('k3', 'not v3'), ('k3', 'v3')}

If any value is not hashable, you lose the ability to treat the dict items views with set operations.

It is not sensible to give indexing support to dict_items views, because the dict does not have an ordering until Python 3.7+, so accessing "the 0th" item would not be well-defined. Even in Python 3.7, where there is a sensible ordering to use for indexing (i.e. the insertion order), it is non-trivial to implement this with O(1) complexity, so it's not supported. The "unwritten rule" is that indexing should be constant-time operation (as it is for list, tuple, dict, str - see here).

user2357112
  • 260,549
  • 28
  • 431
  • 505
wim
  • 338,267
  • 99
  • 616
  • 750
  • 4
    That's actually a much better reason than the one I gave of "It's not supported because there's no support for it" :) – Mad Physicist Oct 19 '18 at 22:11
  • 2
    wim: How did you learn all this about the undocumented type? Just by experiments or perhaps reading the CPython source code? – martineau Oct 19 '18 at 22:12
  • 1
    Good question..reading the mailing lists, seeing the comments from CPython core developers on bugs.python.org, [finding bugs in the implementation](https://stackoverflow.com/q/35784258/674039) etc. – wim Oct 19 '18 at 22:17
  • 3
    Good answer. To appreciate `it is non-trivial to implement this with O(1) complexity`, see [this answer](https://stackoverflow.com/a/52507957/9209546) and the comments. – jpp Oct 19 '18 at 23:38