'Iterator for custom class in Python 3

I'm trying to port a custom class from Python 2 to Python 3. I can't find the right syntax to port the iterator for the class. Here is a MVCE of the real class and my attempts to solve this so far:

Working Python 2 code:

class Temp:
    def __init__(self):
        self.d = dict()
    def __iter__(self):
        return self.d.iteritems()

temp = Temp()
for thing in temp:
    print(thing)

In the above code iteritems() breaks in Python 3. According to this highly voted answer, "dict.items now does the thing dict.iteritems did in python 2". So I tried that next:

class Temp:
    def __init__(self):
        self.d = dict()
    def __iter__(self):
        return self.d.items()

The above code yields "TypeError: iter() returned non-iterator of type 'dict_items'"

According to this answer, Python 3 requires iterable objects to provide a next() method in addition to the iter method. Well, a dictionary is also iterable, so in my use case I should be able to just pass dictionary's next and iter methods, right?

class Temp:
    def __init__(self):
        self.d = dict()
    def __iter__(self):
        return self.d.__iter__
    def next(self):
        return self.d.next

This time it's giving me "TypeError: iter() returned non-iterator of type 'method-wrapper'".

What am I missing here?



Solution 1:[1]

As the error message suggests, your __iter__ function does not return an iterator, which you can easily fix using the built-in iter function

class Temp:
    def __init__(self):
        self.d = {}
    def __iter__(self):
        return iter(self.d.items())

This will make your class iterable. Alternatively, you may write a generator yourself, like so:

def __iter__(self):
    for key,item in self.d.items():
        yield key,item

If you want to be able to iterate over keys and items separately, i.e. in the form that the usual python3 dictionary can, you can provide additional functions, for example

class Temp:
    def __init__(self, dic):
        self.d = dic

    def __iter__(self):
        return iter(self.d)

    def keys(self):
        return self.d.keys()

    def items(self):
        return self.d.items()

    def values(self):
        return self.d.values()

I'm guessing from the way you phrased it that you don't actually want the next() method to be implemented if not needed. If you would, you would have to somehow turn your whole class into an iterator and somehow keep track of where you are momentarily in this iterator, because dictionaries themselves are not iterators. See also this answer.

Solution 2:[2]

I don't know what works in Python 2. But on Python 3 iterators can be most easily created using something called a generator. I am providing the name and the link so that you can research further.

class Temp:
    def __init__(self):
        self.d = {}
    def __iter__(self):
        for thing in self.d.items():
            yield thing

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Abhishek Kumar