Until now, all the data in your programs was written inside the code itself. In this lesson your scripts will start to converse with the user: you'll learn to ask for data with input() (and to deal with the fact that it always returns text), to control print()'s output with its sep and end parameters, and to format results professionally with f-strings (alignment, widths, decimals). It all culminates in Papyrus's first truly interactive program: the new-book entry card. Input and output is the visible face of any program; mastering it turns your scripts from "exercises" into "tools".

Contents

  1. Reading data from the user: input()
  2. input() always returns str: converting the input
  3. print() in depth: the sep and end parameters
  4. Advanced formatting with f-strings: decimals, widths and alignment
  5. Project: an interactive book card for Papyrus

Reading data from the user: input()

The input() function pauses the program, displays a message (the prompt) and waits for the user to type something and press Enter. Whatever was typed is returned as the result, and the usual thing is to store it in a variable:

name = input("What's your name? ")
print(f"Welcome to Papyrus, {name}")

Execution (what the user types goes after the prompt):

What's your name? Luis
Welcome to Papyrus, Luis

Anatomy of the line name = input("What's your name? "):

  • input(...) — calls the reading function.
  • "What's your name? " — the message the user sees. Notice the trailing space: without it, the cursor would sit glued to the question mark. It's a small detail that adds a lot of polish.
  • name = ... — stores what was typed so it can be used later.

The program stays blocked on the input() line until the user presses Enter. You can chain several questions and they'll be asked in order:

title = input("Book title: ")
author = input("Author: ")
print(f"Noted: {title}, by {author}")

input() always returns str: converting the input

This is the critical concept of the lesson: input() always returns a string (str), even if the user types a number.

>>> stock = input("How many copies? ")
How many copies? 5
>>> stock
'5'
>>> type(stock)
<class 'str'>
>>> stock + 1
TypeError: can only concatenate str (not "int") to str

The user typed 5, but the variable contains '5' (text). Trying to operate on it as a number fails or, worse, produces absurd results:

>>> stock * 2      # it repeats the text, it doesn't multiply the number!
'55'

The solution is the type conversion you learned in lesson 01-03, applied right at reading time:

stock = int(input("How many copies? "))       # text -> integer
price = float(input("Price in euros? "))      # text -> decimal

It reads from the inside out: first input() gets the text, then int() or float() converts it.

The user types Code The variable contains Type
5 input(...) '5' str
5 int(input(...)) 5 int
12.50 float(input(...)) 12.5 float
hello int(input(...)) ValueError

The last row matters: if the user types something that isn't a number, the program stops with a ValueError. For now we'll assume well-meaning users; in module 7 (exceptions) you'll learn to catch that error and ask again gracefully.

A complete example for Papyrus:

# stock_value.py - how much money is on the shelf?
price = float(input("Book price: "))
units = int(input("Units on the shelf: "))

total_value = price * units
print(f"Shelf value: {total_value:.2f} euros")
Book price: 12.50
Units on the shelf: 4
Shelf value: 50.00 euros

print() in depth: the sep and end parameters

You already know that print() displays values. Two new things. The first: it can receive several values separated by commas, and it displays them separated by a space:

>>> print("The Odyssey", 12.50, "euros")
The Odyssey 12.5 euros

The second: print() accepts two special parameters that control its behavior:

Parameter What it controls Default value
sep The separator between the values " " (a space)
end What is written at the end "\n" (line break)

They're given by name, at the end of the call:

print("The Odyssey", "Homer", "12.50", sep=" | ")
The Odyssey | Homer | 12.50

With sep="-" you'd get The Odyssey-Homer-12.50; with sep="", everything stuck together. It's the quick way to build tabular lines or codes.

As for end: by default every print() ends with a line break ("\n" is the special character that represents it). By changing it, you can make the next print() continue on the same line:

print("Checking stock", end="... ")
print("OK")
Checking stock... OK

Both parameters can be combined:

print("Papyrus", "Neighbourhood", "Bookshop", sep=" · ", end=" ***\n")
Papyrus · Neighbourhood · Bookshop ***

A useful trick with sep: printing a single-line receipt from loose pieces of data:

date = "2026-07-13"
title = "Hamlet"
total = 9.95
print(date, title, total, sep=" ; ")   # 2026-07-13 ; Hamlet ; 9.95

This style of line with separators will ring a bell when you work with CSV files in module 6.

Advanced formatting with f-strings: decimals, widths and alignment

In lesson 01-03 you saw basic f-strings and the :.2f format. The general syntax is {value:specification}, and the specification lets you control decimals, width and alignment. The essentials:

Decimals

price = 12.5
print(f"{price:.2f}")   # 12.50  -> 2 decimals
print(f"{price:.1f}")   # 12.5   -> 1 decimal
print(f"{price:.0f}")   # 12     -> no decimals (rounds)

f means "fixed-point number format" and the number after the dot indicates how many decimals. It rounds, it doesn't truncate: {9.987:.2f} produces 9.99.

Minimum width and alignment

You can reserve a fixed width of characters and decide which way the content aligns:

Specification Meaning f"[{x:...}]" with x = "Ana"
:10 width 10, text to the left (default) [Ana ]
:>10 width 10, aligned to the right [ Ana]
:<10 width 10, aligned to the left [Ana ]
:^10 width 10, centered [ Ana ]

With numbers, the default alignment is to the right, and it can be combined with decimals: :{width}.{decimals}f.

print(f"[{12.5:8.2f}]")    # [   12.50]  -> width 8, 2 decimals

The star use case: aligned tables

By combining widths and alignments, text output turns into readable tables. The Papyrus catalog:

print(f"{'TITLE':<22}{'AUTHOR':<12}{'PRICE':>8}")
print("-" * 42)
print(f"{'The Odyssey':<22}{'Homer':<12}{12.50:>8.2f}")
print(f"{'Hamlet':<22}{'Shakespeare':<12}{9.95:>8.2f}")
print(f"{'Don Quixote':<22}{'Cervantes':<12}{15.90:>8.2f}")
TITLE                 AUTHOR         PRICE
------------------------------------------
The Odyssey           Homer          12.50
Hamlet                Shakespeare     9.95
Don Quixote           Cervantes      15.90

Notice the details:

  • The titles and authors use :<22 and :<12 (text to the left, fixed width): the columns line up vertically.
  • The prices use :>8.2f (right, two decimals): the decimals sit one below the other, as on any serious invoice.
  • Inside the braces you can put a literal ('The Odyssey') or a variable; in real programs they'll be variables.
  • "-" * 42 generates the separator line by repeating the hyphen (the string repetition from lesson 01-03).

Project: an interactive book card for Papyrus

Let's put it all together. Ana wants a small tool for registering new books: the program asks for the data and presents a nicely formatted card, with the total stock value calculated.

# book_card.py - Interactive book entry for Papyrus

STORE_NAME = "PAPYRUS"

print("=" * 40)
print(f"{STORE_NAME:^40}")
print(f"{'Add a book to the catalog':^40}")
print("=" * 40)

# --- Data input ---
title = input("Title: ")
author = input("Author: ")
price = float(input("Price (euros): "))
stock = int(input("Copies received: "))

stock_value = price * stock

# --- Output: formatted card ---
print()                              # blank separator line
print("-" * 40)
print(f"{'BOOK CARD':^40}")
print("-" * 40)
print(f"{'Title:':<16}{title}")
print(f"{'Author:':<16}{author}")
print(f"{'Price:':<16}{price:>8.2f} euros")
print(f"{'Stock:':<16}{stock:>8} copies")
print(f"{'Total value:':<16}{stock_value:>8.2f} euros")
print("-" * 40)
print("Book registered.", "See you soon!", sep=" ")

A sample run:

========================================
                PAPYRUS
       Add a book to the catalog
========================================
Title: The Odyssey
Author: Homer
Price (euros): 12.50
Copies received: 4

----------------------------------------
               BOOK CARD
----------------------------------------
Title:          The Odyssey
Author:         Homer
Price:             12.50 euros
Stock:                 4 copies
Total value:       50.00 euros
----------------------------------------
Book registered. See you soon!

A review of the techniques used:

  • The constant STORE_NAME in uppercase (lesson 01-04).
  • A centered header with :^40 and decorative lines with "=" * 40.
  • Converted input: float() for the price, int() for the stock; the title and author stay as str, which is what they are.
  • A calculation with the received data (stock_value).
  • An aligned card: labels on the left with a fixed width (:<16) and numbers on the right with decimals (:>8.2f), invoice style.
  • An empty print() to leave a blank line, and a final sep as a sign-off.

This script really is the first functional module of the Papyrus system: it asks for real data, calculates and presents results with a professional look. Save it: we'll keep improving it over the coming modules (validating the input, repeating the entry for several books, saving to a file...).

Common Mistakes and Tips

  • Operating on input() without converting: input() always returns str. If you're going to do arithmetic, wrap it in int() or float() at reading time. The typical symptom: '5' * 2 gives '55' or a TypeError shows up when adding.
  • Typing a decimal comma: if the user types 12,50, float() throws a ValueError (Python expects a point: 12.50). Say so in the prompt: "Price (use a decimal point): ".
  • Converting a typed decimal to int: int("12.50") fails with a ValueError (it isn't a valid integer as text). If you expect decimals, use float().
  • Forgetting the trailing space in the prompt: input("Title:") leaves the cursor glued. input("Title: ") breathes.
  • Confusing sep and end: sep goes between the values of a single print(); end goes at the end of the call. If your output unexpectedly comes out all on one line, check whether you touched end.
  • Misaligned tables: if a column wobbles, check that every row uses the same width in the specification (:<22 in all of them, not :<22 in one and :<20 in another).
  • Tip: design the output "by hand" first, on paper or in a comment (how you want the receipt or the card to look), and then write the print() calls. Formatting with a clear visual goal in mind is much easier.

Exercises

Exercise 1

Without running it, write down exactly what this program displays if the user types 3 and then 2:

a = input("First number: ")
b = input("Second number: ")
print(a + b)
print(int(a) + int(b))
print(a, b, sep="")

Exercise 2

Write a script receipt.py that asks for a book's title, its price and how many copies the customer is buying, and displays a Papyrus mini-receipt with this exact look (the numbers, with two decimals and right-aligned in a width of 8):

====== PAPYRUS ======
Hamlet
3 x     9.95
TOTAL:     29.85 EUR

Exercise 3

Write a script that asks the user for their name and year of birth, calculates their approximate age in 2026 and displays: Hello, Ana. In 2026 you turn 34. using a single f-string. Use a constant CURRENT_YEAR = 2026.

Solutions

Solution 1

32
5
32

Line-by-line explanation:

  • print(a + b)32: a and b are strings ('3' and '2'), and + between strings concatenates.
  • print(int(a) + int(b))5: converted to integers, + really adds.
  • print(a, b, sep="")32: prints both strings with no separator. The same visual result as the first line, by a different route.

Solution 2

# receipt.py
title = input("Title: ")
price = float(input("Price: "))
units = int(input("Units: "))

total = price * units

print("=" * 6, "PAPYRUS", "=" * 6, sep=" ")
print(title)
print(f"{units} x {price:>8.2f}")
print(f"TOTAL: {total:>10.2f} EUR")

With the input Hamlet, 9.95 and 3:

====== PAPYRUS ======
Hamlet
3 x     9.95
TOTAL:      29.85 EUR

Key points: the price is read with float() and the units with int(); {price:>8.2f} aligns to the right in 8 characters with 2 decimals; the first line uses sep=" " to separate the three fragments with a space (a single literal "====== PAPYRUS ======" would also work).

Solution 3

# age.py
CURRENT_YEAR = 2026

name = input("Your name: ")
birth_year = int(input("Year of birth: "))

print(f"Hello, {name}. In {CURRENT_YEAR} you turn {CURRENT_YEAR - birth_year}.")

With the input Ana and 1992:

Hello, Ana. In 2026 you turn 34.

Notice that the expression CURRENT_YEAR - birth_year can go directly inside the f-string's braces: no intermediate variable is needed (although creating one would also be correct, and perhaps more readable).

Conclusion

Your programs are now two-way: they read data from the user with input() — always remembering that it returns str and that anything numeric must be converted with int()/float() — and they write results with full control: separators and line endings with sep/end, and professional formatting with f-strings (decimals :.2f, widths and alignments :<, :>, :^). With all of that you've built Papyrus's book entry card, the project's first interactive program.

One last piece remains to close the introductory module: preparing your environment to work like a professional with external libraries. In the next lesson, Virtual Environments and Package Management, you'll learn to isolate each project's dependencies with venv and to install packages with pip.

Python Programming Course

Module 1: Introduction to Python

Module 2: Control Structures

Module 3: Functions and Modules

Module 4: Data Structures

Module 5: Object-Oriented Programming

Module 6: File Handling

Module 7: Error and Exception Handling

Module 8: Advanced Topics

Module 9: Testing and Debugging

Module 10: Web Development with Python

Module 11: Data Science with Python

Module 12: Final Project

© Copyright 2026. All rights reserved