Iterating over dictionary using __getitem__ in python
A for
loop works with iterators, objects you can pass to next
. An object is an iterator if it has a __next__
method.
Neither of your classes does, so Python will first pass your object to iter
to get an iterator. The first thing iter
tries to do is call the object's __iter__
method.
Neither of your classes defines __iter__
, either, so iter
next checks if its object defines __getitem__
. Both of your classes do, so iter
returns an object of type iterator
, whose __next__
method can be imagined to be something like
def __next__(self):
try:
rv = self.thing.__getitem__(self.i)
except IndexError:
raise StopIteration
self.i += 1
return rv
(The iterator holds a reference to the thing which defined __getitem__
, as well as the value of i
to track state between calls to __next__
. i
is presumed to be initialized to 0.)
For Array
, this works, because it has integer indices. For Dictionary
, though, 0
is not a key, and instead of raising an IndexError
, you get a KeyError
with the __next__
method does not catch.
(This is alluded to in the documentation for __getitem__
:
Note for loops expect that an IndexError will be raised for illegal indexes to allow proper detection of the end of the sequence.
)
To make your Dictionary
class iterable, define __iter__
class Dictionary:
def __init__(self):
self.dictionary = {'a' : 1, 'b' : 2, 'c': 3}
def __getitem__(self,key):
return self.dictionary[key]
def __iter__(self):
return iter(self.dictionary)
dict.__iter__
returns a value of type dict_keyiterator
, which is the thing that yields the dict
's keys, which you can use with Dictionary.__getitem__
.
Adding the following method to your Dictionary
class is sufficient to get it to work:
def __iter__(self):
return iter(self.dictionary)
Important note: When creating custom classes that are sequences or mappings, you should implement all of the relevant methods. Otherwise, your code (e.g., x in y
, for x in y
etc) will either be inefficient or broken as you saw in the case of your Dictionary
class.
See for more details: https://docs.python.org/3/reference/datamodel.html?emulating-container-types#emulating-container-types.