I've implemented a python class to generate data as follows:
class Array:
def __init__(self):
self.arr = [1,2,3,4,5,6,7,8]
def __getitem__(self,key):
return self.arr[key]
a = Array()
for i in a:
print(i, end = " ")
It runs as expected and I get following
1 2 3 4 5 6 7 8
However, I want to do the same thing for dictionary. Why am I not able to iterate over dictionary like this?
class Dictionary:
def __init__(self):
self.dictionary = {'a' : 1, 'b' : 2, 'c': 3}
def __getitem__(self,key):
return self.dictionary[key]
d = Dictionary()
for key in d:
print(key, d[key], end=" ")
I expect following output
a 1 b 2 c 3
But when I run above code, I get following error :
Traceback (most recent call last):
File "<ipython-input-33-4547779db7ec>", line 8, in <module>
for key in d:
File "<ipython-input-33-4547779db7ec>", line 6, in __getitem__
return self.dictionary[key]
KeyError: 0
We can iterate over normal dictionary like this : for key in d
. This iterates over all the keys, is it not possible to iterate like this using __getitem__()
?
__getitem__(x, i) . The method __getitem__(self, key) defines behavior for when an item is accessed, using the notation self[key] . This is also part of both the mutable and immutable container protocols. Unlike some other languages, Python basically lets you pass any object into the indexer.
As mentioned above, you can iterate keys by using the dictionary object directly, but you can also use keys() . The result is the same, but keys() may clarify the intent to the reader of the code. The keys() method returns dict_keys . It can be converted to a list with list() .
Using iteritems is a tad bit faster... But the time to create a view is negligable; it is actually slower to iterate over than a list. This means that in Python 3, if you want to iterate many times over the items in a dictionary, and performance is critical, you can get a 30% speedup by caching the view as a list.
There are two ways of iterating through a Python dictionary object. One is to fetch associated value for each key in keys() list. There is also items() method of dictionary object which returns list of tuples, each tuple having key and value.
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__
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With