Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

exhausted iterators - what to do about them?

(In Python 3.1) (Somewhat related to another question I asked, but this question is about iterators being exhausted.)

# trying to see the ratio of the max and min element in a container c
filtered = filter(lambda x : x is not None and x != 0, c)
ratio = max(filtered) / min(filtered)

It took me half hour to realize what the problem is (the iterator returned by filter is exhausted by the time it gets to the second function call). How do I rewrite it in the most Pythonic / canonical way?

Also, what can I do to avoid bugs of this sort, besides getting more experience? (Frankly, I don't like this language feature, since these types of bugs are easy to make and hard to catch.)

like image 884
max Avatar asked Oct 15 '10 06:10

max


People also ask

Are iterators memory efficient?

Iterators will be faster and have better memory efficiency. Just think of an example of range(1000) vs xrange(1000) . (This has been changed in 3.0, range is now an iterator.) With range you pre-build your list, but xrange is an iterator and yields the next item when needed instead.

Are all iterators Iterables?

Note that every iterator is also an iterable, but not every iterable is an iterator. For example, a list is iterable but a list is not an iterator. An iterator can be created from an iterable by using the function iter().

Are iterators lazy in Python?

Iterators are lazy Iterators allow us to both work with and create lazy iterables that don't do any work until we ask them for their next item.

Can iterators go backwards?

C++ Iterators Reverse Iterators A reverse iterator is made from a bidirectional, or random access iterator which it keeps as a member which can be accessed through base() . To iterate backwards use rbegin() and rend() as the iterators for the end of the collection, and the start of the collection respectively.


3 Answers

The itertools.tee function can help here:

import itertools

f1, f2 = itertools.tee(filtered, 2)
ratio = max(f1) / min(f2)
like image 191
Greg Hewgill Avatar answered Oct 11 '22 23:10

Greg Hewgill


you can convert an iterator to a tuple simply by calling tuple(iterator)

however I'd rewrite that filter as a list comprehension, which would look something like this

# original
filtered = filter(lambda x : x is not None and x != 0, c)

# list comp
filtered = [x for x in c if x is not None and x != 0]
like image 26
lunixbochs Avatar answered Oct 11 '22 22:10

lunixbochs


Actually your code raises an exception that would prevent this problem! So I guess the problem was that you masked the exception?

>>> min([])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: min() arg is an empty sequence
>>> min(x for x in ())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: min() arg is an empty sequence

Anyways, you can also write a new function to give you the min and max at the same time:

def minmax( seq ):
    " returns the `(min, max)` of sequence `seq`"
    it = iter(seq)
    try:
        min = max = next(it)
    except StopIteration:
        raise ValueError('arg is an empty sequence')
    for item in it:
        if item < min:
            min = item
        elif item > max:
            max = item
    return min, max
like image 23
Jochen Ritzel Avatar answered Oct 12 '22 00:10

Jochen Ritzel