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