python – different behavior of print(*generator, )

Tags: ,



Question

Please help understand why the two cases act differently although both use a generator (i for i in range(5)).

>>> print(i for i in range(5))
<generator object <genexpr> at 0x7fc409c02900>
>>> print(*(i for i in range(5)))
0 1 2 3 4
>>> print(*(i for i in range(5)), 5)
0 1 2 3 4 5
>>> _r = (i for i in range(5))
>>> print(_r)
<generator object <genexpr> at 0x7fc409c029e0>
>>> print(*_r)
0 1 2 3 4
>>> print(*_r, 5)
5
>>> print(*(_r), 5)
5

Answer

When you use the * operator on a generator expression (or any iterator for that matter), it consumes it:

my_iterator = (i for i in range(3))

second_iterator = iter(list(range(3)))

# splat operator consumes the generator expression
print(*my_iterator)
0 1 2
print(*my_iterator, "Empty!")
Empty!

# splat operator also consumes the iterator
print(*second_iterator)
0 1 2
print(*second_iterator, "Also empty!")
Also empty!

You’d need to recreate it to re-use it, which is what you’re doing in your first example:

s = (i for i in range(3))
print(*s)
0 1 2

# You create a new generator expression/iterator
s = (i for i in range(3))
print(*s)
0 1 2

# And so it isn't empty
# same with the shorthand
print(*(i for i in range(3))
0 1 2

Note on range

Since I’ve used range for this example, it’s important to note that range doesn’t share this behavior, but an iterator over range does:

x = range(3)

print(*x)
0 1 2

print(*x)
0 1 2
# the range object isn't consumed and can be re-used

a = iter(x)

print(*a)
0 1 2

print(*a)

# prints nothing because `a` has been exhausted

More detail can be found in this answer



Source: stackoverflow