LambdaLabTM
Computer Science · Class 11 · More about print()
print()After⏱️ 12 min read

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:

end_demo.py
# 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')
Output
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.

Key Takeaway
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.
Watch Out
You cannot prove 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.

⚙️ Where sep and end actually go

sep fills the gaps between the arguments. end is added once, after the last one.

sep — between
end — after
the three arguments, and the four slots
'LambdaLab'sep'CS'sep11end

Three arguments make two gaps — one fewer than the arguments — and there is always exactly one ending.

what you would write
print('LambdaLab', 'CS', 11)
print('second line')
what appears on the screen
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:

📋 The problem

Print five stars on one line, using a loop.

stars_end.py
# a row of stars on one line, without building a string

for i in range(5):
    print('*', end='')

print()
print('done')
Output
*****
done
Key Takeaway
The bare 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.
stars_end.py — with the bare print() removed
Output
*****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:

triangle_end.py
# 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()
Output
*
**
***
****
*****
Watch Out
Put that 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:
triangle_broken.py
# 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')
Output
**********
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.

Tip
Both ways are correct, and each has its moment. 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.
triangle_end.py

5Both at once

both.py
# 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()
Output
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

Every print() ends with '\n'

Invisible, one character, and the only reason output goes down the screen. end replaces it with whatever you like.

end happens exactly once

Unlike sep, which happens once per gap. One call, one ending — even when the call prints nothing at all.

print() on its own finishes a row

It prints nothing and then its ending, so its whole job is the newline. It is what ends a row built with end=''.

The indentation of that print() is the pattern

Inside the outer loop: one row per round. At the margin: everything on one line, once, at the very end.

Quick Check

print('one', end='') then print('two'). What appears?

Quick Check

A loop prints '*' with end='' five times, and there is no print() afterwards. What is wrong?

Quick Check

In a nested-loop triangle using end='', where must the bare print() go?