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
- Reading data from the user:
input() input()always returnsstr: converting the inputprint()in depth: thesepandendparameters- Advanced formatting with f-strings: decimals, widths and alignment
- 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:
Execution (what the user types goes after the prompt):
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:
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 strThe user typed 5, but the variable contains '5' (text). Trying to operate on it as a number fails or, worse, produces absurd results:
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 -> decimalIt 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")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:
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:
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:
Both parameters can be combined:
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.95This 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.
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.90Notice the details:
- The titles and authors use
:<22and:<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. "-" * 42generates 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_NAMEin uppercase (lesson 01-04). - A centered header with
:^40and decorative lines with"=" * 40. - Converted input:
float()for the price,int()for the stock; the title and author stay asstr, 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 finalsepas 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 returnsstr. If you're going to do arithmetic, wrap it inint()orfloat()at reading time. The typical symptom:'5' * 2gives'55'or aTypeErrorshows up when adding. - Typing a decimal comma: if the user types
12,50,float()throws aValueError(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 aValueError(it isn't a valid integer as text). If you expect decimals, usefloat(). - Forgetting the trailing space in the prompt:
input("Title:")leaves the cursor glued.input("Title: ")breathes. - Confusing
sepandend:sepgoes between the values of a singleprint();endgoes at the end of the call. If your output unexpectedly comes out all on one line, check whether you touchedend. - Misaligned tables: if a column wobbles, check that every row uses the same width in the specification (
:<22in all of them, not:<22in one and:<20in 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):
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
Line-by-line explanation:
print(a + b)→32:aandbare 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:
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:
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
- Introduction to Python
- Setting Up the Development Environment
- Python Syntax and Basic Data Types
- Variables and Constants
- Basic Input and Output
- Virtual Environments and Package Management
Module 2: Control Structures
Module 3: Functions and Modules
- Defining Functions
- Function Arguments
- Lambda Functions
- Modules and Packages
- Standard Library Overview
Module 4: Data Structures
Module 5: Object-Oriented Programming
Module 6: File Handling
Module 7: Error and Exception Handling
- Introduction to Exceptions
- Handling Exceptions
- Raising Exceptions
- Custom Exceptions
- Best Practices and Error Logging
Module 8: Advanced Topics
- Type Hints
- Decorators
- Generators
- Context Managers
- Concurrency: Threads and Processes
- Asyncio for Asynchronous Programming
Module 9: Testing and Debugging
- Introduction to Testing
- Unit Testing with unittest
- Testing with pytest
- Test-Driven Development
- Debugging Techniques
- Using pdb for Debugging
Module 10: Web Development with Python
- Introduction to Web Development
- Flask Framework Fundamentals
- Building REST APIs with Flask
- Introduction to Django
- Building Web Applications with Django
Module 11: Data Science with Python
- Introduction to Data Science
- NumPy for Numerical Computing
- Pandas for Data Manipulation
- Matplotlib for Data Visualization
- Introduction to Machine Learning with scikit-learn
