-1

I am trying to understand in which situations an iterable can be replaced with an iterator in Python 3.

More specifically, consider this example:

ml = [1,2,3,4]

iter_ml = iter(ml)

deq_ml = collections.deque(ml)
deq_iter_ml = collections.deque(iter_ml)

print(ml)
print(iter_ml)
print(deq_ml)
print(deq_iter_ml)

This produces the output

[1, 2, 3, 4]
<list_iterator object at 0x7f6ee8eef4c0>
deque([1, 2, 3, 4])
deque([1, 2, 3, 4])

If I check the documentation of deque, it accepts an iterable as the first argument. But here when I provided an iterator over the iterable, that worked too

However, it didn't work when the iterator is given to print

Same is the confusion with islice. It works with both iterables and iterators.

How to know if those can be used interchangeably?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
mittal
  • 915
  • 10
  • 29
  • 1
    Maybe [this](https://stackoverflow.com/questions/9884132/what-exactly-are-iterator-iterable-and-iteration) can help. – rchome Nov 28 '21 at 09:32
  • 1
    What do you mean it didn't work with print? Print does print the iterator's representation, just as it prints the iterable's. Although note that the iterator can only be consumed _once_, so print gets an _empty_ iterator (not that it matters here). – jonrsharpe Nov 28 '21 at 09:33
  • @jonrsharpe , what I meant was that `print` didn't print the list. While `deque` worked with both `iterable` or and `iterator` over that iterable – mittal Nov 28 '21 at 09:34
  • 1
    It did print the list. If you mean it didn't print the items from the iterator, then I'd note that you didn't ask it to, you asked it to print _the iterator itself_ (which was actually empty at that point anyway). `deque` _iterates over_ the argument, so anything iterable (which iterators are) can be consumed. – jonrsharpe Nov 28 '21 at 09:35
  • @jonrsharpe I think my confusion was stemming from the question that would be the behavior when `iter` is called on `iterator`. Calling `iter` on `iterable` returns and `iterator` which `deque` and `islice` would be doing internally. But I think I got my answer. Since `iter` on `iterator` return `self`, the methods which operate on an `iterable` would still work fine – mittal Nov 28 '21 at 10:57

2 Answers2

0

An iterable is any type that implements __iter__. An iterator is a specific kind of iterable, namely one that also implements __next__.
As such, an iterator can be used anywhere an iterable is expected. Usually, that means iterating something to get values: for loops, list/tuple/deque/... constructors, iterator tools like map, and more.

However, that does not mean that an iterator is the exact same thing as any iterable. For example, a list is an iterable and a list's iterator is an iterable, but they are two distinct objects.

>>> from collections.abc import Iterable
>>> l = [0, 1, 4, 9, 16]
>>> isinstance(l, Iterable)
True
>>> isinstance(iter(l), Iterable)
True
>>> l is iter(l)
False

As a comparison, both 3 and 3.0 are numbers. They represent the same value, can often be used interchangeably and one can convert from one to the other. However, they are still distinct things and do look and behave differently in many contexts – for example, being displayed differently.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
0

Thank you for the links and comments.

I think the source of my confusion was this:

Calling iter on iterable will return an iterator.

But what would be the behavior when iter was called on an iterator. As that would be the case when I replace iterable with iterator in deque and islice

Confusion cleared

As, one the links from the comments above state

An ITERATOR is an object that is self-iterable (meaning that it has an iter method that returns self).

I got my answer. Also this class post by Vincent nicely depicts that each iterator is always an iterable.

enter image description here

mittal
  • 915
  • 10
  • 29