r.ook
r.ook

Reputation: 13888

iter() returned non-iterator of type 'dict_items'

This is driving me insane. Below is a class I was messing around with, the goal was to have a way to support dynamic attribute assignment. Please forget for a moment the poor implementation or why I didn't just use a dictionary, I know there are other ways to do this (such as Enum) but this was my test code before and now it became my quest to find out why it's not working.

class Family(object):
    def children(self):
        return self.__dict__.values()

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, item):
        return self.__dict__[item]

    def __repr__(self):
        return str(self.__dict__)

    def __iter__(self):
        return self.__dict__.items()

This all seems fine, and I initiated a test object as follows:

foo = Family()
foo.a = 'foo'
foo.b = 'bar'

[i for i in foo]

Now the expected outcome was [('a', 'foo'), ('b', 'bar')], but what I got was this instead:

TypeError: iter() returned non-iterator of type 'dict_items'

I thought I might have messed up the __iter__ method, so I checked as follow:

type(foo.__iter__())
# <class 'dict_items'>
type(foo.__dict__.items())
# <class 'dict_items'>

[i for i in foo.__dict.__items()]
# [('a', 'foo'), ('b', 'bar')]

foo.__dict__.items() == foo.__iter__()
# True

If they're both identical, why doesn't [i for i in foo] work?

Upvotes: 8

Views: 5351

Answers (1)

shawn
shawn

Reputation: 4363

What about this:

def __iter__(self):
    return iter(self.__dict__.items())

Python wants an iterator, not a list (or other list-like/dict-like object)

Remember: a list or dict is not an iterator, an iterator is usually (for easy-understanding) a pointer (cursor) pointing to an item of the list or dict.

Upvotes: 17

Related Questions