py3k printing generators -- not!

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sat Jun 6 18:23:32 CEST 2009


On Sat, 06 Jun 2009 05:28:30 -0700, samwyse wrote:

> The one thing that's killing me in Python 3000 

Python 3000 was vapourware. When the vapour condensed into liquid, it was 
renamed Python 3. Right now, the only vapourware is Python4000, which may 
or may not be created by Guido's heir some time in the 2020s.


> is that every time I try
> to print something, it seems like I get <generator object <genexpr> at
> 0x01BAF508>.

"Every time"? Really? Even when you print an object which isn't a 
generator?


> Googling only found one reference, a posting elsewhere by
> one Carl Johnson (aka carlj7,
> http://www.artima.com/forums/flat.jsp?forum=106&thread=211200#275387),
> which apparently was never answered.

Ignoring Carl's totally pointless "mycond()" function, he apparently 
wants type(iterable)(i for i in iterable) to give iterable:

>>> it = [1, 2]
>>> type(it)(i for i in it)
[1, 2]


But that can't work for arbitrary iterables:

>>> it = {1:2, 3:4}
>>> type(it)(i for i in it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot convert dictionary update sequence element #0 to a 
sequence


Nor will it work for generator objects themselves:

>>> it = (1+x for x in range(5))
>>> type(it)(i for i in it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create 'generator' instances


So Carl's suggestion can't be applied universally, it can only hold for 
some iterables. It doesn't even hold for all sequences, with strings a 
conspicuous example. In fact, that's what Carl is complaining about:

>>> it = "abc"
>>> type(it)(i for i in it)
'<generator object <genexpr> at 0xb7ce3d24>'


He apparently would prefer str(i for i in "abc") to return "abc". 
However, you seem to prefer "a,b,c" instead. So what you would prefer, 
and what Carl would prefer, are different.

Either way though, there's a fatal flaw in the idea: printing an object 
shouldn't consume the object, but that's what you want. Unlike a list or 
a tuple, a generator is *code*, not a data type. It produces values when 
and as asked. So there's no way to peek inside a generator and see the 
values that it will produce, consequently, for str() to behave as you and 
Carl want, it has to run the generator to completion, performing an 
arbitrarily large amount of work (and perhaps, for non-terminating 
generators, never finishing!) before you can print it. And then, having 
printed it, the generator is now exhausted. Try to print it again, and 
you'll get the empty string.

Calling list() on a generator is different: it is *designed* to exhaust 
the generator. I'd be upset if print() and/or str() did the same thing.

I'd also be upset if generators looked like a string when they're not:

>>> x = (c.lower() for c in "ABC")
>>> x
'abc'
>>> x.upper()  # x looks like a string, but it isn't
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'generator' object has no attribute 'upper'



[...]
> I've
> thought about defining my own function "prnt" that wraps print and fixes
> generators, but that requires me to get their type, which despite the
> claims of "help(type(x for x in range(0)))" cannot be found in builtins.
>  How are other solving this?

I'm not "solving" this, because I don't think this is a problem that 
needs solving. But if you want a custom print function, this should work:

def print(*values, **kwargs):
    from builtins import print as pr
    gen = type(x for x in [1,2])
    values = [','.join(str(s) for s in obj) if type(obj) is gen 
             else obj for obj in values]
    pr(*values, **kwargs)




-- 
Steven



More information about the Python-list mailing list