In the previous lesson you used names like price or stock to store values. Those names are variables, and in this lesson we study them in depth: what exactly happens when you assign, which names are valid and which ones the PEP 8 style guide recommends, how to assign several values at once, how constants are expressed in Python, and a first look at two ideas that will accompany you throughout the course: variable scope and the difference between reassigning and mutating. Naming and organizing data well is half the job of programming; the Papyrus system will start to look like a real program.

Contents

  1. What a variable is and how assignment works
  2. Naming rules (what Python demands)
  3. Naming conventions: PEP 8 and snake_case
  4. Multiple assignment and swapping values
  5. Constants by convention
  6. Basic scope at script level
  7. Mutability vs reassignment (a conceptual introduction)

What a variable is and how assignment works

A variable is a name associated with a value. It's created with the assignment operator =:

store = "Papyrus"
odyssey_price = 12.50
odyssey_stock = 4

The right way to read store = "Papyrus" is: "the name store now refers to the value "Papyrus"". The order matters: the name on the left, the value on the right. The other way round ("Papyrus" = store) is a syntax error.

A nuance that is more literal in Python than in other languages: a variable is not a box that contains the value, but a label pointing at the value. The value exists in memory, and the name is a reference to it:

flowchart LR
    A["store"] --> V1["'Papyrus' (str)"]
    B["odyssey_price"] --> V2["12.50 (float)"]
    C["odyssey_stock"] --> V3["4 (int)"]

This image of "labels on values" will serve you well in the final section of this lesson and, later on, with the lists of module 4.

Once created, you use a variable by writing its name:

print(store)                        # Papyrus
print(odyssey_price * odyssey_stock) # 50.0 -> stock value in euros

And it can be reassigned: made to point at a new value, forgetting the previous one:

odyssey_stock = 4
odyssey_stock = 3        # one copy has been sold
print(odyssey_stock)     # 3

It's very common to reassign a variable using its own current value:

odyssey_stock = odyssey_stock - 1   # sell another copy

There's no mathematical paradox here: first the right-hand side is evaluated (3 - 12) and then the name is made to point at the result. Python offers shortcuts for these operations, the augmented assignment operators:

Shortcut Equivalent to Typical use in Papyrus
x += n x = x + n a delivery arrives: stock += 10
x -= n x = x - n selling: stock -= 1
x *= n x = x * n doubling a price
x /= n x = x / n applying half price

Remember from the previous lesson: don't confuse = (assign) with == (compare).

Finally, using a variable that doesn't exist yet produces an error:

print(discount)   # NameError: name 'discount' is not defined

Variables must be assigned before they're used: scripts run from top to bottom.

Naming rules (what Python demands)

Python imposes a few hard rules on variable names. If you break them, the program won't even start (SyntaxError):

  • Only letters, digits and underscores (_). No spaces, hyphens - or symbols.
  • They cannot start with a digit: 2books is invalid; books2 is valid.
  • They cannot be reserved words of the language: if, for, while, and, or, not, True, False, None, class, def, import, return... (there are 35; the REPL lists them for you with help("keywords")).
  • Uppercase and lowercase are distinguished: price, Price and PRICE are three different variables.
Name Valid? Reason
odyssey_stock Yes Letters and underscore
book2 Yes The digit isn't at the start
2book No Starts with a digit
odyssey-stock No The hyphen - is the subtraction operator
book price No Spaces aren't allowed
class No Reserved word
café Yes (legal) Python 3 allows accented characters, but avoid it (next section)

Naming conventions: PEP 8 and snake_case

Beyond the mandatory rules, there is Python's official style guide, PEP 8 (Python Enhancement Proposal 8), which recommends how you should write code so it's readable and consistent. Its most relevant naming conventions for now:

  • Variables and functions: snake_case — all lowercase, words separated by underscores: odyssey_price, sale_total, customer_name.
  • Constants: UPPERCASE_WITH_UNDERSCORES: BOOK_VAT, STORE_NAME (we'll see them shortly).
  • Classes: PascalCase (each word capitalized): Book, RegularCustomer — you'll use it in module 5.
  • Avoid names with accented characters even though they're legal, and avoid a lone l, a lone uppercase O or a lone uppercase I (they get confused with 1 and 0).
Style Example Used for
snake_case price_with_vat variables and functions
UPPERCASE BOOK_VAT constants
PascalCase SecondHandBook classes (module 5)
camelCase priceWithVat not used in Python (it belongs to Java/JavaScript)

Just as important as the format is expressiveness. Compare:

# Bad: what is each thing?
x = 12.50
y = 3
z = x * y

# Good: the code explains itself
unit_price = 12.50
units_sold = 3
sale_total = unit_price * units_sold

Both snippets do the same thing, but anyone can read (and maintain) the second one without guessing. Practical rule: the name should say what the variable contains, not how it's calculated or what type it is.

Multiple assignment and swapping values

Python lets you assign several variables in a single line, separating both sides with commas:

title, price, stock = "Hamlet", 9.95, 6

It's equivalent to three assignments, but more compact when the values are related (here, the three fields of a book record). There must be the same number of names as of values, or Python throws a ValueError.

You can also assign the same value to several names:

monday_sales = tuesday_sales = 0   # both start at zero

And the crown jewel: swapping values without an auxiliary variable. In many languages swapping two variables requires a third, temporary one; in Python:

shelf_a = "The Odyssey"
shelf_b = "Hamlet"

# Ana rearranges the window display: the books change shelves
shelf_a, shelf_b = shelf_b, shelf_a

print(shelf_a)   # Hamlet
print(shelf_b)   # The Odyssey

It works because Python first evaluates the whole right-hand side (with the old values) and then assigns to the left. This mechanism, called unpacking, will give us a lot more mileage with the tuples of module 4.

Constants by convention

A constant is a value that shouldn't change while the program runs: a VAT rate, the shop's name, a fixed discount.

Here Python is peculiar: it has no real constants. There is no keyword that prevents a variable from being reassigned. Instead, the community uses a convention: names in UPPERCASE_WITH_UNDERSCORES mean "this is a constant, don't touch it".

# Constants of the Papyrus system (they go at the top of the script)
STORE_NAME = "Papyrus"
BOOK_VAT = 0.04              # 4% (fictional, for the examples)
MEMBER_DISCOUNT = 0.05       # 5% for member customers

# Normal use
base_price = 12.50
final_price = base_price * (1 + BOOK_VAT)
print(f"{STORE_NAME}: final price {final_price:.2f} euros")
Papyrus: final price 13.00 euros

Key points:

  • Python won't stop you from writing BOOK_VAT = 0.21 further down; it's just that no self-respecting programmer does it. It's a contract between humans.
  • Constants are declared at the top of the script, grouped together, so whoever reads the code finds the configuration values in a single place.
  • A huge advantage: if the VAT rate changes, you modify it in one line, instead of hunting for 0.04 scattered across the whole program. Loose numeric values repeated through the code are disparagingly called magic numbers; constants are their antidote.

Basic scope at script level

The scope of a variable is the region of the program where that name exists and can be used. Since we're not writing functions yet (they arrive in module 3), your scripts work in a single scope, called the module's global scope, and the rules are simple:

  • A variable exists from the line where it's assigned until the end of the script.
  • Using it before that line produces a NameError.
  • The same happens in the REPL: variables live from the moment you define them until you close the session.
# card.py
print(STORE_NAME)          # NameError: it doesn't exist yet

STORE_NAME = "Papyrus"
print(STORE_NAME)          # now it does: Papyrus

Two practical consequences:

  • Logical order: define first (constants and data at the top), use afterwards. A well-organized script reads from top to bottom like a recipe.
  • Careful with reusing names: if you call three different things price throughout the script, the variable will keep changing value and the code will be confusing. Better to use specific names: odyssey_price, hamlet_price.

When functions arrive you'll see that they create their own local scope (variables that only exist inside the function); for now, hold on to the idea that where you define a variable determines where you can use it.

Mutability vs reassignment (a conceptual introduction)

We finish with a conceptual distinction that will save you a lot of confusion later. There are two ways in which "something changes" in a program:

Concept What changes Example
Reassignment The label starts pointing at another value. The old value isn't modified. stock = 5 and then stock = 4
Mutation The value itself is modified from the inside, without changing labels. Adding a book to a list (module 4)

All the types you know so far — int, float, str, bool, None — are immutable: their values cannot be modified from the inside. When you do stock = stock - 1, you're not "lowering" the number 5; you're creating/obtaining the number 4 and moving the label onto it. The 5 remains 5, untouchable.

With strings it's very easy to see:

title = "the odyssey"
title.upper()          # produces "THE ODYSSEY"... but doesn't change title
print(title)           # the odyssey  (still the same!)

title = title.upper()  # to "change it" you have to REASSIGN
print(title)           # THE ODYSSEY

Operations on immutable types return new values; if you want to keep the result, reassign.

And mutation? It will appear in module 4 with lists: a list can indeed be modified from the inside (adding, removing, reordering elements) without reassigning the variable. Then the image of "labels pointing at values" will be crucial, because two labels can point at the same list and the changes will be visible from both. For now, the idea is enough:

  • Reassigning = moving the label to another value (always possible).
  • Mutating = changing the value from the inside (only with mutable types, which we haven't seen yet).

Common Mistakes and Tips

  • NameError: name '...' is not defined: you're using the variable before assigning it, or you've misspelled it (remember that Price and price are different).
  • Cryptic names: p, x2, data. Two weeks from now not even you will know what they were. Invest five seconds in a good name; you'll get them back many times over.
  • camelCase in Python: finalPrice works, but it clashes with the entire ecosystem. In Python, snake_case: final_price.
  • Clobbering a constant: writing BOOK_VAT = 0.21 halfway through a script is legal but breaks the uppercase contract. If a value changes during execution, it wasn't a constant: name it in lowercase.
  • Shadowing Python's names: avoid calling your variables print, str, type... It's legal, but it "covers up" the original function (print = 5 would make print("hello") fail for the rest of the script).
  • Expecting title.upper() to change title: strings are immutable; the result has to be reassigned.
  • Tip: when in doubt about whether a name is good, read it in a sentence: "the unit_price times the units_sold gives the sale_total". If the sentence sounds natural, the names are good.

Exercises

Exercise 1

State whether each name is invalid (Python raises an error), legal but contrary to PEP 8, or correct for a normal variable:

  1. sale-total
  2. SaleTotal
  3. sale_total
  4. 2nd_book
  5. _discount
  6. for

Exercise 2

Write a script for Papyrus that:

  1. Defines the constants STORE_NAME = "Papyrus" and MEMBER_DISCOUNT = 0.05.
  2. Creates, with multiple assignment on a single line, the variables title, price and stock with the values "Don Quixote", 15.90 and 8.
  3. Simulates selling 2 copies to Luis (who is a member): update stock with an augmented assignment operator and calculate the total with the discount applied.
  4. Displays, with f-strings, the remaining stock and the total charged with two decimals.

Exercise 3

Ana has mislabelled two books and their prices need to be swapped. Starting from these variables, swap them without using any auxiliary variable and display the result:

odyssey_price = 9.95    # should be Hamlet's
hamlet_price = 12.50    # should be The Odyssey's

Then answer: after running odyssey_price = 12.50, has the value 9.95 been mutated? Why?

Solutions

Solution 1

  1. sale-totalinvalid: the hyphen is the subtraction operator; Python reads it as sale - total.
  2. SaleTotallegal but contrary to PEP 8: it's PascalCase, reserved by convention for classes.
  3. sale_totalcorrect: perfect snake_case.
  4. 2nd_bookinvalid: it can't start with a digit.
  5. _discountlegal: a leading underscore is valid (by convention it suggests "internal use", as you'll see in module 5); for a normal variable, discount would do.
  6. forinvalid: reserved word.

Solution 2

# member_sale.py
STORE_NAME = "Papyrus"
MEMBER_DISCOUNT = 0.05

title, price, stock = "Don Quixote", 15.90, 8

# Sale of 2 copies to Luis (member)
units = 2
stock -= units
total = price * units * (1 - MEMBER_DISCOUNT)

print(f"{STORE_NAME}: sold {units} x {title}")
print(f"Remaining stock: {stock}")
print(f"Total charged: {total:.2f} euros")
Papyrus: sold 2 x Don Quixote
Remaining stock: 6
Total charged: 30.21 euros

stock -= units is equivalent to stock = stock - units; and (1 - MEMBER_DISCOUNT) is 0.95, i.e. paying 95% of the amount.

Solution 3

odyssey_price = 9.95
hamlet_price = 12.50

odyssey_price, hamlet_price = hamlet_price, odyssey_price

print(f"The Odyssey: {odyssey_price:.2f} euros")   # 12.50
print(f"Hamlet: {hamlet_price:.2f} euros")         # 9.95

Python first evaluates the entire right-hand side (12.50, 9.95, with the old values) and then assigns it to the left, achieving the swap in one line.

Answer to the question: there is no mutation. float values are immutable: 9.95 still exists as it was; the only thing that has happened is that the label odyssey_price now points at another value (12.50). It's a reassignment.

Conclusion

You now master the central piece of any program: variables as labels pointing at values, assignment (simple, augmented and multiple), the naming rules and the PEP 8 conventions (snake_case for variables, UPPERCASE for constants), the basic scope of a script read from top to bottom, and the conceptual difference between reassigning a label and mutating a value. Papyrus now has configuration constants and its first discounted sale to Luis.

Until now, all the data was written inside the code. In the next lesson, Basic Input and Output, the program will come to life: you'll learn to ask the user for data with input(), to format the output professionally, and you'll build Papyrus's first interactive program.

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