CoursePython from Scratch to Intermediate · Module 1: Getting Started · part 11 of 75
Part 11 · Module 1: Getting Started

Lab: Print Amira's first shelf card

10 min read·14 Oct 2026
Module 1 · Lab · About 45 minutes · Needs: lessons 1 to 3 of this module

What you are building

THE NAMES THE SCRIPT THE CARD rules LOAN_DAYS ... book title, author, year loan L04, Jonah, day 14 card.py !python card.py BOOKNOOK SHELF CARD Frankenstein by Mary Shelley Loan L04 held by Jonah Due on shelf day 28 ... 16 days late
The rules, the book and the loan become names (grey), a script uses them (blue, your code), and it prints the same correct card every run (green).

Amira's question for this module was "Can the computer print a card for one book?" In this lab you answer it properly. The card is for Frankenstein, loan L04, the copy Jonah borrowed on Monday 14 September 2026 and still has today, Wednesday 14 October.

In Lesson 1 every fact on the card was typed straight into the text. Here every fact lives in a variable first, and the card is printed from those names. The numbers are worked out by Python, not by you: the due day and the days late come from the shelf rules. At the end you save the whole thing as a script, card.py, so the same card comes out every time anyone runs it. Use one new Colab notebook for the whole lab and run the cells in order.

Step 1: Start a fresh notebook and check Python

A NEW NOTEBOOK [1] !python --version Python 3.12.15 names remembered none yet 3.12 is what matters
A fresh notebook holds no names yet, and its first cell confirms you are on Python 3.12, the version every output in this lab came from.

A fresh notebook remembers nothing, which is exactly what you want: every name the card uses will be made in this lab. Open a new notebook in Colab, name it module-01-lab.ipynb, and check which Python you have before you write anything else.

python
!python --version

Code walkthrough

  • ! sends the line to the computer's command line, outside the notebook.
  • python --version asks the python program for its version and does nothing else. This is the same Python that runs card.py in Step 6.

You should see:

text
Python 3.12.15

The last number may differ. 3.12 is what matters: every output in this lab was captured with it. If yours starts 3.13 or higher, the lab still works.

Step 2: Name the shelf's rules

FIVE NAMES FOR THE RULES LOAN_DAYS 14 days FEE_PER_DAY 20 pence a day FEE_CAP 300 pence MAX_BOOKS 3 books TODAY_DAY 44 shelf day
Each rule is a constant in capitals with its unit beside it, so 20 always reads as pence a day and 44 as a shelf day.

The card depends on the shelf's rules, so they come first. Each rule gets a constant: a name in capital letters that the program promises never to change. These four names are fixed for the whole course. A fifth, TODAY_DAY, holds today as a shelf day. A shelf day counts days from the opening, 1 September, so 14 October is day 44.

python
LOAN_DAYS = 14     # a loan lasts 14 days
FEE_PER_DAY = 20   # late fee, in pence, for each day after the due date
FEE_CAP = 300      # the most one loan can be charged, in pence
MAX_BOOKS = 3      # the most books one member may have out at once
TODAY_DAY = 44     # Wednesday 14 October 2026, as a shelf day
print(LOAN_DAYS, FEE_PER_DAY, FEE_CAP, MAX_BOOKS, TODAY_DAY)

Code walkthrough

  • Each line is an assignment: = makes the name on the left point at the number on the right.
  • Everything after # is a comment, which Python skips. Each comment says what the number measures and its unit, so nobody reads 20 as pounds.
  • print shows the five values on one line, with a space between each.

You should see:

text
14 20 300 3 44

Step 3: Hold the book

TITLE, AUTHOR, YEAR = "FRANKENSTEIN", "MARY SHELLEY", 1818 title "Frankenstein" str author "Mary Shelley" str year 1818 int
Unpacking pairs names with values by position, first with first, and the year has no quotation marks, so it is an int, not text.

Next, the book itself. Its title, author and year belong together, so name them in one line with unpacking: three names on the left of =, three values on the right, paired by position. Then ask Python for each value's type, because the card will treat text and numbers differently.

python
title, author, year = "Frankenstein", "Mary Shelley", 1818
print(title, "by", author, year)
print(type(title))
print(type(year))

Code walkthrough

  • Line 1 makes three names at once. The first name gets the first value, and so on.
  • "Frankenstein" and "Mary Shelley" are in quotation marks, so they are text, a str. 1818 has none, so it is a whole number, an int.
  • type(...) reports the type of the value inside the brackets.

You should see:

text
Frankenstein by Mary Shelley 1818
<class 'str'>
<class 'int'>

Read <class 'int'> as "this is an int".

Step 4: Hold the loan and work out its days

LOAN L04 IN SHELF DAYS + 14 loan 16 days late day 14 borrowed day 28 due day 44 today
The due day is the borrow day plus 14, and the days late are today minus the due day: 28 and 16 for Jonah (red marks the late stretch).

Now the loan. Four facts come straight from the record: the loan id, the member, the borrow date in words, and the borrow date as a shelf day. Two more are worked out from them. The due day is the borrow day plus the loan period. The days late are today minus the due day.

python
loan_id = "L04"
member = "Jonah"
borrowed = "Monday 14 September 2026"
borrowed_day = 14                        # shelf day: 14 September is day 14
due_day = borrowed_day + LOAN_DAYS
days_late = TODAY_DAY - due_day
print(loan_id, member, borrowed)
print("due on day", due_day, "and", days_late, "days late")

Code walkthrough

  • borrowed is text for people to read. borrowed_day is a number Python can count with. They describe the same day, and the card uses both.
  • borrowed_day + LOAN_DAYS is an expression: Python works it out to one value, 28, and due_day points at that value.
  • TODAY_DAY - due_day works out to 16. The name days_late says what the number means.

You should see:

text
L04 Jonah Monday 14 September 2026
due on day 28 and 16 days late
BY HAND, FIRST PYTHON, AFTER 14 + 14 = 28 44 - 28 = 16 due_day: 28 days_late: 16 they agree, so the card can be trusted
Working the numbers by hand before Python does gives you something to compare with; when both say 28 and 16 (green), the answer is checked.

These two numbers agree with Lesson 1: Jonah's book was due back on Monday 28 September, and today it is 16 days late. Write a number down by hand before Python works it out, and you will always know whether to trust the answer. The incident file for this module shows why that habit matters.

Step 5: Print the card in the notebook

PRINT("LOAN", LOAN_ID, "HELD BY", MEMBER) "Loan" Loan loan_id L04 "held by" held by member Jonah text as typed; names become their values; one space between
In one print, quoted text comes out as typed and each name (grey) is replaced by its value, with one space between the parts.

Every fact now has a name, so the card is only print lines. Each line mixes text you type, in quotation marks, with names, which print replaces with their values. A comma separates the parts, and print puts one space between them.

python
print("BOOKNOOK SHELF CARD")
print(title)
print("by", author)
print("first published", year)
print("Loan", loan_id, "held by", member)
print("Borrowed", borrowed)
print("Due on shelf day", due_day)
print("Days late on shelf day", TODAY_DAY, "is", days_late)
print("Late fee:", FEE_PER_DAY, "pence a day, at most", FEE_CAP, "pence")
print("Limit:", MAX_BOOKS, "books per member")

Code walkthrough

  • print(title) has no quotation marks, so Python prints the value title points at, Frankenstein, not the word "title".
  • The last two lines print the rules from their constants, so the card can never disagree with the rules.

You should see:

text
BOOKNOOK SHELF CARD
Frankenstein
by Mary Shelley
first published 1818
Loan L04 held by Jonah
Borrowed Monday 14 September 2026
Due on shelf day 28
Days late on shelf day 44 is 16
Late fee: 20 pence a day, at most 300 pence
Limit: 3 books per member

The fee itself is missing on purpose. Sixteen days at 20 pence would be 320, but the cap stops it at 300; choosing the smaller of two numbers is Module 2's job.

Step 6: Save the card as a script and run it

CARD.PY, READ TOP TO BOTTOM 1. the rules 5 constants 2. the book and the loan 7 names, 2 worked out 3. the card 10 print lines made before used
The script holds everything the card needs in order: the rules, then the book and loan, then the print lines, so every name exists before it is used.

The card in Step 5 works only because Steps 2 to 4 ran first in this session. Close the tab and those names are gone. A script, a .py file, keeps everything together. %%writefile saves the cell as a file instead of running it, so the cell must hold every name the card uses, then the print lines.

python
%%writefile card.py
# Amira's first shelf card: loan L04, asked for on Wednesday 14 October 2026
LOAN_DAYS = 14     # a loan lasts 14 days
FEE_PER_DAY = 20   # late fee, in pence, for each day after the due date
FEE_CAP = 300      # the most one loan can be charged, in pence
MAX_BOOKS = 3      # the most books one member may have out at once
TODAY_DAY = 44     # Wednesday 14 October 2026, as a shelf day

title, author, year = "Frankenstein", "Mary Shelley", 1818
loan_id = "L04"
member = "Jonah"
borrowed = "Monday 14 September 2026"
borrowed_day = 14                        # shelf day: 14 September is day 14
due_day = borrowed_day + LOAN_DAYS
days_late = TODAY_DAY - due_day

print("BOOKNOOK SHELF CARD")
print(title)
print("by", author)
print("first published", year)
print("Loan", loan_id, "held by", member)
print("Borrowed", borrowed)
print("Due on shelf day", due_day)
print("Days late on shelf day", TODAY_DAY, "is", days_late)
print("Late fee:", FEE_PER_DAY, "pence a day, at most", FEE_CAP, "pence")
print("Limit:", MAX_BOOKS, "books per member")

Code walkthrough

  • %%writefile card.py must be the very first line. Nothing below it runs now; it is saved into card.py.
  • The script is Steps 2 to 5 in one place: rules first, then the book and loan, then the card. A name must be made before the line that uses it.
  • The blank lines are only for the reader. Python skips them.

You should see:

text
Writing card.py
!PYTHON CARD.PY card.py the file a new Python no notebook names 10 card lines every run first line to last, once, then it stops
Running a script starts a new Python that knows only what is in the file, reads it once from top to bottom, and prints the ten card lines.

Now run it. !python card.py starts a brand new Python, which reads the file from its first line to its last, once.

python
!python card.py

Code walkthrough

  • python card.py runs the file. This new Python knows nothing about the notebook's names; everything it uses is inside card.py.

You should see:

text
BOOKNOOK SHELF CARD
Frankenstein
by Mary Shelley
first published 1818
Loan L04 held by Jonah
Borrowed Monday 14 September 2026
Due on shelf day 28
Days late on shelf day 44 is 16
Late fee: 20 pence a day, at most 300 pence
Limit: 3 books per member

The same ten lines as Step 5. Run the cell again tomorrow and you get them again.

Check your work

THE CHECK CELL card.py kept output 10 lines 10 lines? True heading right? True 16 days late? True no error? True
The check runs the script, keeps what it prints and asks four yes-or-no questions; four True answers (green) mean the card is right.

Paste this cell as it is. It runs card.py the way !python does, keeps what it prints, and asks four yes-or-no questions about it. Later modules explain every part; for now, read the answers.

python
import subprocess, sys
result = subprocess.run([sys.executable, "card.py"], capture_output=True, text=True)
lines = result.stdout.splitlines()
print(len(lines) == 10)
print(lines[0] == "BOOKNOOK SHELF CARD")
print(lines[7] == "Days late on shelf day 44 is 16")
print(result.stderr == "")

Code walkthrough

  • subprocess.run(...) runs card.py with the notebook's own Python and keeps its output in result.
  • lines is that output cut into separate lines. lines[0] is the first line and lines[7] the eighth: Python counts from 0.
  • == asks "are these two the same?" and gives True or False.
  • result.stderr holds any error message. Empty means card.py ran without one.

You should see:

text
True
True
True
True

A False points at one question. Compare your card.py with Step 6 one character at a time, run the %%writefile cell again, then this cell.

Break it

SHORT_CARD.PY IN A NEW PYTHON print("BOOKNOOK SHELF CARD") BOOKNOOK SHELF CARD print(title) NameError: title title was made in the notebook, not in this file
The script prints its heading, then stops with a NameError (red, an error) because title was made in the notebook, never in the file.

Try saving only the print lines, as if the script could borrow the notebook's names. It fails on purpose, so it is shown as text: type the first cell, then run the script in a second cell with !python short_card.py.

text
%%writefile short_card.py
print("BOOKNOOK SHELF CARD")
print(title)

Run this and you will see:

text
BOOKNOOK SHELF CARD
Traceback (most recent call last):
  File "/content/short_card.py", line 2, in <module>
    print(title)
          ^^^^^
NameError: name 'title' is not defined. Did you mean: 'tuple'?

The output was captured with Python 3.12. Read it from the last line up. A NameError means Python met a name it does not know. But title exists in your notebook: you made it in Step 3. The script ran in a brand new Python, which never saw that cell. The heading did print first, because a NameError is found only when Python reaches the line. The "Did you mean" guess, tuple, is just the nearest name Python knows, and no help here.

NOTEBOOK SCRIPT remembers names in the order cells ran lost when the session ends holds every name in the file itself same card any day, any computer
A notebook's names depend on what ran and when (amber, uncertain); a script carries every name with it, so its card is the same every time (green).

That is the difference between the two places your code can live. A notebook remembers names between cells, in the order you ran them. A script remembers nothing it does not contain. This is why card.py holds every rule and every fact, and why it gives the same card on any computer, any day.

What you added to the Booknook shelf

  • The four shelf rules as constants, LOAN_DAYS, FEE_PER_DAY, FEE_CAP and MAX_BOOKS, plus TODAY_DAY = 44.
  • Frankenstein and loan L04 held in variables, with the due day (28) and days late (16) worked out by Python.
  • card.py, a script that prints Amira's first shelf card the same way every time, and a check that proves it.

Going further

  • Make a second card for Lena's loan L23: Jane Eyre, Charlotte Brontë, 1847, borrowed Tuesday 29 September 2026, shelf day 29. You should get due day 43 and 1 day late.
  • Change LOAN_DAYS to 21 in a copy of card.py and predict the days late before you run it.
  • Add a comment to card.py above due_day that says why the due day is what it is.