Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python builtin "all" with generators

Tags:

python

numpy

I have the following problem with python's "all" and generators:

G = (a for a in [0,1])
all(list(G))   # returns False - as I expected

But:

G = (a for a in [0,1])
all(G)         # returns True!

Can anybody explain that?

UPDATE: I swear I get this! Check this out:

In [1]: G = (a for a in [0,1])

In [2]: all(G)
Out[2]: True

I am using Python 2.6.6 with IPython 0.10.2, all installed within the Python(x,y) package. Strangely enough, I get "True" (above) when I use the Spider IDE, and "False" in pure console...

UPDATE 2: As DSM pointed out, it seems to be a numpy problem. Python(x,y) loads numpy, and all(G) was actually calling numpy.all(G) rather then the builtin all(). A quick workaround is to write:

__builtins__.all(G)

Thank you all for your help!

-maciej

like image 497
maciej Avatar asked Sep 20 '11 21:09

maciej


People also ask

Are generators in Python useful?

Generators have been an important part of Python ever since they were introduced with PEP 255. Generator functions allow you to declare a function that behaves like an iterator. They allow programmers to make an iterator in a fast, easy, and clean way.

When were generators added to Python?

Generators were introduced in PEP 255, together with the yield statement. They have been available since Python version 2.2.

Is every iterator a generator in Python?

Every generator is an iterator, but not vice versa. A generator is built by calling a function that has one or more yield expressions ( yield statements, in Python 2.5 and earlier), and is an object that meets the previous paragraph's definition of an iterator .

Are generators faster than lists Python?

List comprehensions are usually faster than generator expressions as generator expressions create another layer of overhead to store references for the iterator. However, the performance difference is often quite small.


2 Answers

Aha!

Does Python(x,y) happen to import numpy? [It looks like it.]

Python 2.7.2 (v2.7.2:8527427914a2, Jun 11 2011, 15:22:34) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> 
>>> G = (a for a in [0,1])
>>> all(G)
False
>>> from numpy import all
>>> 
>>> G = (a for a in [0,1])
>>> all(G)
True
>>> 

Here's an explanation by Robert Kern:

It [all --ed] works on arrays and things it can turn into arrays by calling the C API equivalent of numpy.asarray(). There's a ton of magic and special cases in asarray() in order to interpret nested Python sequences as arrays. That magic works fairly well when we have sequences with known lengths; it fails utterly when given an arbitrary iterator of unknown length. So we punt. Unfortunately, what happens then is that asarray() sees an object that it can't interpret as a sequence to turn into a real array, so it makes a rank-0 array with the iterator object as the value. This evaluates to True.

like image 60
DSM Avatar answered Oct 03 '22 06:10

DSM


No, it doesn't. The following snippet returns False

G = (a for a in [0,1])
all(G)         # returns False

Are you perhaps doing the following

G = (a for a in [0,1])
all(list(G))   # returns False
all(G)         # returns True!

In that case, you are exhausting the generator G when you construct the list, so the final call to all(G) is over an empty generator and hence returns the equivalent of all([]) -> True.

A generator can't be used more than once.

like image 44
donkopotamus Avatar answered Oct 03 '22 05:10

donkopotamus