3

With iter(), I can do this:

>>> listWalker = iter ( [23, 47, 'hike'] )
>>> for x in listWalker: print x,

But I could do this anyway:

>>> listWalker = [23, 47, 'hike']
>>> for x in listWalker: print x,

What value does it add?

lang2
  • 11,433
  • 18
  • 83
  • 133
  • There's a great example of using `iter` [here](http://stackoverflow.com/questions/2233204/how-does-zipitersn-work-in-python). – GWW Feb 23 '14 at 15:26
  • Related: [Why does a Python Iterator need an iter method that simply returns self?](http://stackoverflow.com/questions/21343327/why-does-a-python-iterator-need-an-iter-method-that-simply-returns-self) – Ashwini Chaudhary Feb 23 '14 at 15:28
  • Actually, that "related" question has a spot-on top-voted answer. I'm leaning to agree with the vote to close. – Charles Duffy Feb 23 '14 at 15:34
  • @CharlesDuffy This is not a duplicate. The linked question asked why `iter(some_iterator)` works. This question asks why there **is** an `iter` function in the first place (when `for` does the job anyway). The two are not the same. – user4815162342 Feb 24 '14 at 07:39
  • @user4815162342, the questions indeed aren't the same, but the answers are entirely applicable. – Charles Duffy Feb 24 '14 at 13:44
  • @CharlesDuffy The accepted/top-voted answer doesn't appear applicable at all. It explains what `iter` *does* without explaining what would be the *point* of needing to do that. (It also explains why `iter` needs `__iter__`, which is even less relevant for this question.) – user4815162342 Feb 24 '14 at 13:49
  • Since my initial vote-to-close, some much better (and on-point) answers have turned up here, so I'm inclined to agree that it was in error. – Charles Duffy Feb 24 '14 at 13:54

4 Answers4

6

In addition to using iter to explicitly get an iterator for an object that implements the __iter__ method, there is the lesser-known two-argument form of iter, which makes an iterator which repeatedly calls a function until it returns a given sentinel value.

 for line in iter(f.readline, 'EOF'):
     print line

The preceding code would call f.read (for, say, an open file handle f) until it reads a line consisting of the string EOF. It's roughly the same as writing

for line in f:
    if line == "EOF":
        break
    print line

Additionally, an iterator may be a distinct object from the object it iterates over. This is true for the list type. That means you can create two iterators, both of which iterate independently over the same object.

itr1 = iter(mylist)
itr2 = iter(mylist)

x = next(itr1)   # First item of mylist
y = next(itr1)   # Second item of my list
z = next(itr2)   # First item of mylist, not the third

File handles, however, act as their own iterator:

>>> f = open('.bashrc')
>>> id(f)
4454569712
>>> id(iter(f))
4454569712

In general, the object returned by iter depends on the __iter__ method implemented by the object's type.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    +1 Excellent answer, covering both "normal" usage of `iter` and the two-argument form. You might want to be more precise by stating that an iterator **can** be a distinct object from the object it iterates over. Many objects, most notably file objects, but also iterators themselves, are their own iterators. – user4815162342 Feb 25 '14 at 08:47
5

The point of iter is that it allows you to obtain the iterator from an iterable object and use it yourself, either to implement your own variant of the for loop, or to maintain the state of the iteration across multiple loops. A trivial example:

it = iter(['HEADER', 0, 1, 2, 3])  # coming from CSV or such
title = it.next()
for item in it:
    # process item
    ...

A more advanced usage of iter is provided by this grouping idiom:

def in_groups(iterable, n):
    """Yield element from iterables grouped in tuples of size n."""
    it = iter(iterable)
    iters = [it] * n
    return zip(*iters)
user4815162342
  • 141,790
  • 18
  • 296
  • 355
0

When you're doing a for loop on a variable, it implicitly call the __iter__ method of the iterable you passed in fact.
You're always using iter() is some way when you're looping over lists, tuples... and every iterable.

I think this extract of byte-code can convince you:

>>> def a():
...     for x in [1,2,3]:
...         print x
...
>>> import dis
>>> dis.dis(a)
  2           0 SETUP_LOOP              28 (to 31)
              3 LOAD_CONST               1 (1)
              6 LOAD_CONST               2 (2)
              9 LOAD_CONST               3 (3)
             12 BUILD_LIST               3
             15 GET_ITER                                 # <--- get iter is important here
        >>   16 FOR_ITER                11 (to 30)
             19 STORE_FAST               0 (x)

  3          22 LOAD_FAST                0 (x)
             25 PRINT_ITEM
             26 PRINT_NEWLINE
             27 JUMP_ABSOLUTE           16
        >>   30 POP_BLOCK
        >>   31 LOAD_CONST               0 (None)
             34 RETURN_VALUE


But, iterables allows you also some other things in Python, such as the use of next() to walk into an iterable, or raising a StopIteration exception. It can be useful if you're dealing with different object types and you want to apply a generic algorithm.
Maxime Lorant
  • 34,607
  • 19
  • 87
  • 97
0

From the docs:

iter(o[, sentinel])

[...] Without a second argument, o must be a collection object which supports the iteration protocol (the __iter__() method), or it must support the sequence protocol (the __getitem__() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised. [...]

So it constructs an iterator from an object.

As you say, this is done automatically in loops and comprehensions but some times you want to get an iterator and handle it directly. Just keep it in the back of your mind until you need it.

When using the second argument:

If the second argument, sentinel, is given, then o must be a callable object. The iterator created in this case will call o with no arguments for each call to its next() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.

This is useful for many things but particularly so for legacy style functions like file.read(bufsize) which has to be called repeatedly until it returns "". That can be converted to an iterator with iter(lambda : file.read(bufsize), ""). Nice and clean!

Javier
  • 2,752
  • 15
  • 30