print function and unwanted trailing space

C

candide

What is the equivalent in Python 3 to the following Python 2 code:

# -----------------------------
for i in range(5):
print i,
# -----------------------------

?

Be careful that the above code doesn't add a trailing space after the
last number in the list, hence the following Python 3 code isn't
strictly equivalent:


# -----------------------------
for i in range(5):
print(i, end=' ') # <- The last ' ' is unwanted
print()
# -----------------------------
 
A

Andreas Perstinger

What is the equivalent in Python 3 to the following Python 2 code:

# -----------------------------
for i in range(5):
print i,
# -----------------------------

?

How about
0 1 2 3 4

Bye, Andreas
 
C

candide

Le 31/08/2013 10:43, Andreas Perstinger a écrit :
How about

0 1 2 3 4


Thanks for your answer. The output is stricly the same but the code
doesn't suit my needs :

1) I'm porting to Python 3 a Python 2 full beginner course : the
learners are not aware of the join method nor the str type nor
generators stuff;
2) Your code introduce a (sometimes) useless conversion to str (consider
a string instead of range(5)).
 
P

Peter Otten

candide said:
Le 31/08/2013 10:43, Andreas Perstinger a écrit :



Thanks for your answer. The output is stricly the same but the code
doesn't suit my needs :

1) I'm porting to Python 3 a Python 2 full beginner course : the
learners are not aware of the join method nor the str type nor
generators stuff;
2) Your code introduce a (sometimes) useless conversion to str (consider
a string instead of range(5)).

You are out of luck, the softspace mechanism, roughly

softspace = False
for i in range(5):
if softspace:
print(end=" ")
print(i, end="")
softspace = True
print()

with `softspace` saved as a file attribute, is gone in Python3. But I don't
think that someone who doesn't know it existed will miss the feature.

Maybe you can allow for /some/ magic and introduce your students to

print(*range(5))

early-on.
 
S

Steven D'Aprano

What is the equivalent in Python 3 to the following Python 2 code:

# -----------------------------
for i in range(5):
print i,
# -----------------------------

?

Be careful that the above code doesn't add a trailing space after the
last number in the list,

Of course it does. Have you actually tried it? The interactive
interpreter is tricky, because you cannot directly follow a for-loop with
another statement. If you try, the interactive interpreter gives you an
indentation error. But we can work around it by sticking everything
inside an if block, like so:

py> if True:
.... for i in range(5):
.... print i,
.... # could be pages of code here
.... print "FINISHED"
....
0 1 2 3 4 FINISHED


Or you could stick the code inside an exec, which doesn't have the same
limitation as the interactive interpreter. This mimics the behaviour of
code in a file:

py> exec """for i in range(5):
.... print i,
.... print "FINISHED"
.... """
0 1 2 3 4 FINISHED


The same results occur with any other Python 2.x, and indeed all the way
back to Python 1.5 and older.

hence the following Python 3 code isn't strictly equivalent:


# -----------------------------
for i in range(5):
print(i, end=' ') # <- The last ' ' is unwanted
print()


The last space is exactly the same as you get in Python 2. But really,
who cares about an extra invisible space? In non-interactive mode, the
two are exactly the same (ignoring the extra print() call outside the
loop), but even at the interactive interpreter, I'd like to see the code
where an extra space makes a real difference.
 
N

Ned Batchelder

What is the equivalent in Python 3 to the following Python 2 code:

# -----------------------------
for i in range(5):
print i,
# -----------------------------

?

Be careful that the above code doesn't add a trailing space after the
last number in the list, hence the following Python 3 code isn't
strictly equivalent:


# -----------------------------
for i in range(5):
print(i, end=' ') # <- The last ' ' is unwanted
print()
# -----------------------------

For a beginner course, the trailing space is fine, use this code.
They'll never notice the trailing space (I wouldn't have!) and to
explain why the comma leaves off the last one takes a really advanced
understanding of obscure details anyway.

--Ned.
 
P

Peter Otten

Steven said:
Of course it does. Have you actually tried it? The interactive
interpreter is tricky, because you cannot directly follow a for-loop with
another statement. If you try, the interactive interpreter gives you an
indentation error. But we can work around it by sticking everything
inside an if block, like so:

py> if True:
... for i in range(5):
... print i,
... # could be pages of code here
... print "FINISHED"
...
0 1 2 3 4 FINISHED


Or you could stick the code inside an exec, which doesn't have the same
limitation as the interactive interpreter. This mimics the behaviour of
code in a file:

py> exec """for i in range(5):
... print i,
... print "FINISHED"
... """
0 1 2 3 4 FINISHED


The same results occur with any other Python 2.x, and indeed all the way
back to Python 1.5 and older.

Your test is flawed. The softspace mechanism ensures that there is a space
*between* all printed items, but not *after* the last printed item.

print "FINISHED"

will add a space while

print

will not. Compare:
.... for i in range(3): print >> f, i,
.... print >> f
........ for i in range(3): print >> f, i,
.... print >> f, "FINISHED"
....'0 1 2 FINISHED\n'
 
C

candide

Le 31/08/2013 13:16, Steven D'Aprano a écrit :
Of course it does. Have you actually tried it?


Of course I did, redirecting the output to a file in order to spot an
eventually trailing space. I did the same for the Python 3 code.
 
C

candide

Le 31/08/2013 12:31, Peter Otten a écrit :
softspace = False
for i in range(5):
if softspace:
print(end=" ")
print(i, end="")
softspace = True
print()


The if instruction imposes useless testing (we know in advance the
problem to occur at the very end of the loop) and useless writing
(writing '').

The following is clearer

# -------------------------
n=5
for i in range(n-1):
print(i, end=' ')
print(n-1)
# -------------------------


but doesn't solve all the cases (imagine a string or an iterator).
 
C

candide

Le 31/08/2013 13:24, Ned Batchelder a écrit :
For a beginner course, the trailing space is fine, use this code.

I was really expecting there was a trick but I'll follow your advice,
after all the trailing space is invisible!

Nevertheless, this can be quite annoying. For instance, some automated
program testing (cf. http://www.spoj.com/, http://acm.timus.ru/, etc)
expect the exact output to get accepted, no byte more or a byte less.
 
P

Peter Otten

candide said:
Le 31/08/2013 12:31, Peter Otten a écrit :


The if instruction imposes useless testing (we know in advance the
problem to occur at the very end of the loop) and useless writing
(writing '').

To make it crystal clear, the above was to illustrate the algorithm used in
Python 2, not a suggestion. Python 2 uses that "useless testing" -- which is
cheap compared to actual I/O.
The following is clearer

# -------------------------
n=5
for i in range(n-1):
print(i, end=' ')
print(n-1)
# -------------------------


but doesn't solve all the cases (imagine a string or an iterator).

I still think you should live with a trailing space or go with my actual
suggestion

print(*range(5))
 
S

Steven D'Aprano

Le 31/08/2013 13:16, Steven D'Aprano a écrit :




Of course I did, redirecting the output to a file in order to spot an
eventually trailing space. I did the same for the Python 3 code.


Fair enough. Seems like the print statement implementation in Python 2 is
uglier than I imagined, keeping hidden state between invocations.
 
C

Chris Angelico

The if instruction imposes useless testing (we know in advance the problem
to occur at the very end of the loop) and useless writing (writing '').

The following is clearer

# -------------------------
n=5
for i in range(n-1):
print(i, end=' ')
print(n-1)
# -------------------------


but doesn't solve all the cases (imagine a string or an iterator).

Similar but maybe simpler, and copes with more arbitrary iterables:

it=iter(range(5))
print(next(it), end='')
for i in it:
print('',i, end='')

Also guarantees to use 'sep' between the elements, fwiw.

ChrisA
 
O

Oscar Benjamin

Similar but maybe simpler, and copes with more arbitrary iterables:

it=iter(range(5))
print(next(it), end='')
for i in it:
print('',i, end='')

If you want to work with arbitrary iterables then you'll want

it = iter(iterable)
try:
val = next(it)
except StopIteration:
pass # Or raise or something?
else:
print(val, end='')
for i in it:
print('', i, end='')


Oscar
 
C

candide

Le 31/08/2013 15:59, Peter Otten a écrit :

To make it crystal clear, the above was to illustrate the algorithm used in
Python 2, not a suggestion.


Ok sorry, I misinterpreted.



I still think you should live with a trailing space


Are you sure ? The following code

#----------------------------------
import io

output = io.StringIO()
n=5
for i in range(n-1):
print(i, end=' ', file=output)
print(n-1, file=output)
print(output.getvalue().count(' '))
#----------------------------------


outputs 4, the correct number of space character.


or go with my actual
suggestion

print(*range(5))


It's a very good suggestion (the best one in fact) but rather
complicated to explain to pure novices in programming.
 
C

Chris Angelico

If you want to work with arbitrary iterables then you'll want

it = iter(iterable)
try:
val = next(it)
except StopIteration:
pass # Or raise or something?
else:
print(val, end='')
for i in it:
print('', i, end='')

I went with this version:

except StopIteration:
raise

In other words, if it's going to bomb, let it bomb :)

ChrisA
 
J

Joshua Landau

I went with this version:

except StopIteration:
raise

In other words, if it's going to bomb, let it bomb :)

I think the point is that StopIteration is an unsafe error to raise.
 
T

Terry Reedy

I think the point is that StopIteration is an unsafe error to raise.

It should only be raised by iterator.__next__ method and caught by
iterator user and not re-raised. Raise something like "ValueError('empty
iterable') from None" instead.
 
W

Wayne Werner

# -----------------------------
for i in range(5):
print(i, end=' ') # <- The last ' ' is unwanted
print()
# -----------------------------

Then why not define end='' instead?

-W
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top