The end Argument
Two print() calls put their output on two lines. Nothing in either line says so — the line break comes from a character print() adds at the end, which you have never seen because it is invisible by design. end is how you replace it.
1The character you have never seen
After the last value, print() adds '\n' — the newline from the escape-sequence section of the strings lesson. It is one character, it cannot be seen, and it is the entire reason output goes down the screen instead of along it:
# what print() adds after the last argument
print('one')
print('two')
print('one', end='')
print('two')
print('one', end=' ')
print('two')
print('one', end=' -> ')
print('two')one two onetwo one two one -> two
print('one')No end written, so the default is used: '\n'. That is why 'two' appears underneath rather than beside.
print('one', end='')Ends with nothing at all, so the next print() carries on from exactly where this one stopped — onetwo, with no gap, because nothing was put between them.
print('one', end=' -> ')The ending does not have to be short or invisible. Whatever string you give it is written after the last value, once.
end is used exactly once, whatever else is in the call. That is the difference from sep, which is used once per gap and can be used zero times. Every print() has exactly one ending — the only question is what it is.end did anything with one line. An empty ending shows up only when something else prints afterwards — which is why every example on this page has a second print(). If your program ends with end='', the shell prompt appears on the same line and it looks like a bug.2The two arguments, in their two places
Now both halves. Change end and watch the second line move — that is the only visible evidence an ending exists at all.
sep fills the gaps between the arguments. end is added once, after the last one.
Three arguments make two gaps — one fewer than the arguments — and there is always exactly one ending.
LambdaLab CS 11 second line
The second print() starts on a new line, because end is still the newline nobody can see.
3What end is really for: a loop on one line
Until now, anything printed inside a loop went down the screen, one item per line, and the way round it was to build a string with + and print it once. Here is the other way:
Print five stars on one line, using a loop.
# a row of stars on one line, without building a string
for i in range(5):
print('*', end='')
print()
print('done')***** done
print() after the loop is not decoration. Five print() calls that end with nothing leave the cursor sitting after the fifth star. print() with no arguments at all prints nothing and then its ending — so its only job is that newline, and it is what finishes the row.*****done
4The star triangle, written the other way
The nested-loop pattern programs built each row into a string and printed it once. With end the same triangle can be printed a character at a time — and the row-finishing print() moves to exactly where the old print(line) was:
# the star triangle, written with end instead of a collector
n = 5
for i in range(1, n + 1):
for j in range(1, i + 1):
print('*', end='')
print()* ** *** **** *****
print() one level out and the triangle collapses. At the margin it runs once, after both loops, so all the stars land on one line and only then does the cursor move:# forgetting the empty print() at the end of the row
n = 4
for i in range(1, n + 1):
for j in range(1, i + 1):
print('*', end='')
print()
print('finished')********** finished
Ten stars — 1 + 2 + 3 + 4 — in a single line. The indentation of that empty print() is the whole difference between a triangle and a stripe, and it is the mistake this way of writing patterns invites.
end='' is shorter and closer to how you would say it out loud. Building the row into line gives you the row as a value — which you can measure, store or change before printing — and it is what a question expecting a “line” variable is looking for. Write both once and you will know which the question wants.5Both at once
# a table on one line each, with sep and end together
for i in range(1, 6):
print(7, 'x', i, '=', 7 * i, sep=' ', end=' ')
print()7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35
Five values with sep between them and a three-space end after them, five times over. There are trailing spaces at the end of that line, invisible but real: the last end is added like every other one, because end does not know it is last.
6Recap
Invisible, one character, and the only reason output goes down the screen. end replaces it with whatever you like.
Unlike sep, which happens once per gap. One call, one ending — even when the call prints nothing at all.
It prints nothing and then its ending, so its whole job is the newline. It is what ends a row built with end=''.
Inside the outer loop: one row per round. At the margin: everything on one line, once, at the very end.
print('one', end='') then print('two'). What appears?
A loop prints '*' with end='' five times, and there is no print() afterwards. What is wrong?
In a nested-loop triangle using end='', where must the bare print() go?