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.