Python Iterables struggling using map() built-in
Ivan Evstegneev
webmailgroups at gmail.com
Sat Dec 6 11:44:17 EST 2014
Hello everyone,
I'm currently in the process of self-study journey, so I have some questions
arisen from time to time.
Today I would like to talk about iterables and iterators,(ask for your help
actually ^_^).
Before I'll continue, just wanted to draw your attention to the fact that I
did some RTFM before posting.
Links:
1. map() built-in definitions:
https://docs.python.org/3/library/functions.html#map - for Python 3.X
https://docs.python.org/2.6/library/functions.html#map - for Python 2.6.X
2. Glossary definitions of "iterable " and "iterator":
https://docs.python.org/3/glossary.html?highlight=glossary
3. Iterator Types:
https://docs.python.org/2/library/stdtypes.html#typeiter
4. iter() definition:
https://docs.python.org/2/library/functions.html#iter
5. Some StackOverflow links, related to the topic:
http://stackoverflow.com/questions/13054057/confused-with-python-lists-are-t
hey-or-are-they-not-iterators
http://stackoverflow.com/questions/9884132/understanding-pythons-iterator-it
erable-and-iteration-protocols-what-exact
http://stackoverflow.com/questions/19523563/python-typeerror-int-object-is-n
ot-iterable
http://stackoverflow.com/questions/538346/iterating-over-a-string
6. And of course, re-read couple of times a relevant parts of the
book ('Learning Python" by Mark Lutz).
But the questions still persist, maybe because those examples look too
esoteric though.
Another warning: Despite all my attempts to make my questions as short as
possible it still looks huge. My apologies.
The problem:
Here is the book's example:
Consider the following clever alternative coding for this chapter's zip
emulation examples,
adapted from one in Python's manuals at the time I wrote these words:
def myzip(*args):
iters = map(iter, args)
while iters:
res = [next(i) for i in iters]
yield tuple(res)
Because this code uses iter and next, it works on any type of iterable. Note
that there
is no reason to catch the StopIteration raised by the next(it) inside the
comprehension
here when any one of the arguments' iterators is exhausted-allowing it to
pass ends
this generator function and has the same effect that a return statement
would. The
while iters: suffices to loop if at least one argument is passed, and avoids
an infinite
loop otherwise (the list comprehension would always return an empty list).
This code works fine in Python 2.X as is:
>>> list(myzip('abc', 'lmnop'))
[('a', 'l'), ('b', 'm'), ('c', 'n')]
But it falls into an infinite loop and fails in Python 3.X, because the 3.X
map returns a
one-shot iterable object instead of a list as in 2.X. In 3.X, as soon as
we've run the list
comprehension inside the loop once, iters will be exhausted but still True
(and res will
be []) forever. To make this work in 3.X, we need to use the list built-in
function to
create an object that can support multiple iterations:
>>>def myzip(*args):
iters = list(map(iter, args)) # Allow multiple scans
...rest as is...
Run this on your own to trace its operation. The lesson here: wrapping map
calls in
list calls in 3.X is not just for display!
*********END OF THE BOOK EXAMPLE************
According to the book , in order to get things work properly in Python 3.X,
I should write this code:
>>> def myzip(*args):
iters = list(map(iter, args))
while iters:
res = [next(i) for i in iters]
yield tuple(res)
And all seemed to be clear here, but, when I tried to run this thing:
>>> k= myzip(1, 2, 3, 4)
>>> next(k)
I got this result:
Traceback (most recent call last):
File "<pyshell#73>", line 1, in <module>
next(k)
File "<pyshell#65>", line 2, in myzip
iters = list(map(iter, args))
TypeError: 'int' object is not iterable
Finding the problem:
I started to investigate further in order to catch the bug:
What I've tried?
1. >>> L = [1, 2, 3, 4]
>>> iter(L) is L
False ---> According to the "theory" it's OK, because list doesn't have
self iterators.
>>> k = iter(L)
>>>print(k)
<list_iterator object at 0x03233F90>
>>> next(k)
1
>>> print(list(map(iter, L)))
Traceback (most recent call last):
File "<pyshell#88>", line 1, in <module>
print(list(map(iter, L)))
TypeError: 'int' object is not iterable
2. I went to strings:
>>> S = 'spam'
>>> iter(S) is S
False ---> Same about strings
>>> string = iter(S)
>>> string
<str_iterator object at 0x02E24F30>
>>> next(string)
's'
and so on.
And then just tried this one:
>>> print(list(map(iter, S)))
[<str_iterator object at 0x02E24FF0>,
<str_iterator object at 0x02E24CF0>,
<str_iterator object at 0x02E24E10>,
<str_iterator object at 0x02E24DF0>] -----> At this moment "Wrecking
Ball" song played in my head %)))))))
>>> k = list(map(iter,S))
>>> next(k[0])
's'
>>> next(k[0])
Traceback (most recent call last):
File "<pyshell#99>", line 1, in <module>
next(k[0])
StopIteration
So I may admit that I found the problem, but can't explain it to myself.
Finally I've checked the map() definition.
According to python.org: "Return an iterator that applies function to every
item of iterable, yielding the results."
The questions:
And as I've promised the question section:
1. What actually map() trying to do in Python 3.X?
I mean, why is this works fine:
>>>L = [1, 2, 3, 4]
>>> k = iter(L)
>>> next(k)
1
and so on.
But not this:
>>> list(map(iter, L))
Traceback (most recent call last):
File "<pyshell#88>", line 1, in <module>
print(list(map(iter, L)))
TypeError: 'int' object is not iterable
2. Why strings are allowed "to become" an iterators(self-iterators)?
Maybe because of files(reading from file) ?
I mean why, is this possible:
>>> print(list(map(iter, S)))
[<str_iterator object at 0x02E24FF0>,
<str_iterator object at 0x02E24CF0>,
<str_iterator object at 0x02E24E10>,
<str_iterator object at 0x02E24DF0>]
What I'm trying to say, is that if I wouldn't tried to run the book's
example with integer arguments(or tuples or lists as arguments) it wouldn't
alarm this issue.
And I've lived happily assuming that I understand iterables. ))))
This is kind of a "dirty trick", because author do not talk about this
anywhere.
3. The last question
Author says:
" But it falls into an infinite loop and fails in Python 3.X, because the
3.X map returns a
one-shot iterable object instead of a list as in 2.X. In 3.X, as soon as
we've run the list
comprehension inside the loop once, iters will be exhausted but still True.
To make this
work in 3.X, we need to use the list built-in function to create an object
that can support
multiple iterations". (Like:"Wat?!" ^_^)
Why the infinite loop would be there and why should list() to make it
finite? o_0
Hope you have not get annoyed too much.
Thanks in advance,
Ivan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20141206/aab57504/attachment.html>
More information about the Python-list
mailing list