Reputation: 11966
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?
Upvotes: 3
Views: 786
Reputation: 531315
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.
Upvotes: 6
Reputation: 155046
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)
Upvotes: 5
Reputation: 2776
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!
Upvotes: 0
Reputation: 36161
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
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.
Upvotes: 0