user3284469
user3284469

Reputation:

Is an iterator also an iterable?

I found that:

>>> a={'x':42, 'y':3.14, 'z':7}
>>> b=a.__iter__()
>>> b.__dir__()
['__next__', ..., '__iter__', ...]
>>> b
<set_iterator object at 0x7efdd4e5afc0>

Does an iterator always have the __iter__ method?

According to https://stackoverflow.com/a/9884259 an iterator also an iterable. If it is true that an iterator always has __iter__ method?

Upvotes: 22

Views: 2437

Answers (4)

wim
wim

Reputation: 362707

An iterator is iterable.

That's documented and explained here:

Iterators are required to have an __iter__() method that returns the iterator object itself so every iterator is also iterable

An iterable is not necessarily an iterator

Iterators must have a __next__ method, by definition. To give a simple counterexample:

>>> ''.__next__
AttributeError: 'str' object has no attribute '__next__'

A string object is an iterable, but is not an iterator.

Upvotes: 1

countunique
countunique

Reputation: 4296

Does an iterator always have the __iter__ method?

Yes. All iterators have an __iter__ method that returns itself. From the docs

iterator.__iter__()

Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements.

Upvotes: 0

Moses Koledoye
Moses Koledoye

Reputation: 78556

An iterator is iterable. And yes, an iterator always has an __iter__ method.

Calling iter on an iterator, which summons the __iter__ hook, returns the same iterator:

>>> it = iter([]) # return iterator from iterable
>>> it is iter(iter(it)) is it.__iter__().__iter__().__iter__()
True

A classical example of method chaining.

As you must have also noticed, most implementations of the iterator protocol for custom classes always follows:

def __iter__(self):
    return self

That is if the iteration is not delegated to another iterator, via say return iter(...).

It would be quite counter-intuitive for an iterator to not implement the iterator protocol don't you think? The __iter__ implementation of the protocol is described below:

iterator.__iter__()

Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements.

[Emphasis mine]

This behaviour is expectedly consistent with iterator objects returned by builtins:

>>> m = map(None, [])
>>> m
<map object at 0x...>
>>> m is m.__iter__().__iter__().__iter__()
True

P.S: I apologise for calling dunders repeatedly; makes it look like it's the right thing to do. But it's not!

Upvotes: 7

MSeifert
MSeifert

Reputation: 152647

An iterable needs to implement an __iter__ method or a __getitem__ method:

An object can be iterated over with for if it implements __iter__() or __getitem__().

An iterator needs a __iter__ method (that returns self) and a __next__ method (I'm not 100% sure about the __next__).

it is true that an iterator always has __iter__ method?

Yes!

This is also documented in the Data model:

object.__iter__(self)

This method is called when an iterator is required for a container. This method should return a new iterator object that can iterate over all the objects in the container. For mappings, it should iterate over the keys of the container.

Iterator objects also need to implement this method; they are required to return themselves. For more information on iterator objects, see Iterator Types.

(Emphasis mine)

As to your second question:

Is an iterator also an iterable?

Yes, because it has a __iter__ method.

Additional notes

Besides the formal implementation it's easy to check if something is iterable by just checking if iter() can be called on it:

def is_iterable(something):
    try:
        iter(something)
    except TypeError:
        return False
    else:
        return True

Likewise it's possible to check if something is an iterator by checking if iter() called on something returns itself:

def is_iterator(something):
    try:
        return iter(something) is something  # it needs to return itself to be an iterator
    except TypeError:
        return False

But don't use them in development code, these are just for "visualization". Mostly you just iterator over something using for ... in ... or if you need an iterator you use iterator = iter(...) and then process the iterator by calling next(iterator) until it throws a StopIteration.

Upvotes: 19

Related Questions