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
- What a variable is and how assignment works
- Naming rules (what Python demands)
- Naming conventions: PEP 8 and snake_case
- Multiple assignment and swapping values
- Constants by convention
- Basic scope at script level
- 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 =:
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:
And it can be reassigned: made to point at a new value, forgetting the previous one:
It's very common to reassign a variable using its own current value:
There's no mathematical paradox here: first the right-hand side is evaluated (3 - 1 → 2) 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:
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:
2booksis invalid;books2is 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 withhelp("keywords")). - Uppercase and lowercase are distinguished:
price,PriceandPRICEare 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 uppercaseOor a lone uppercaseI(they get confused with1and0).
| 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_soldBoth 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:
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:
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 OdysseyIt 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")Key points:
- Python won't stop you from writing
BOOK_VAT = 0.21further 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.04scattered 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: PapyrusTwo 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
pricethroughout 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 ODYSSEYOperations 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 thatPriceandpriceare 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:
finalPriceworks, but it clashes with the entire ecosystem. In Python,snake_case:final_price. - Clobbering a constant: writing
BOOK_VAT = 0.21halfway 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 = 5would makeprint("hello")fail for the rest of the script). - Expecting
title.upper()to changetitle: 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_pricetimes theunits_soldgives thesale_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:
sale-totalSaleTotalsale_total2nd_book_discountfor
Exercise 2
Write a script for Papyrus that:
- Defines the constants
STORE_NAME = "Papyrus"andMEMBER_DISCOUNT = 0.05. - Creates, with multiple assignment on a single line, the variables
title,priceandstockwith the values"Don Quixote",15.90and8. - Simulates selling 2 copies to Luis (who is a member): update
stockwith an augmented assignment operator and calculate the total with the discount applied. - 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:
Then answer: after running odyssey_price = 12.50, has the value 9.95 been mutated? Why?
Solutions
Solution 1
sale-total— invalid: the hyphen is the subtraction operator; Python reads it assale - total.SaleTotal— legal but contrary to PEP 8: it's PascalCase, reserved by convention for classes.sale_total— correct: perfect snake_case.2nd_book— invalid: it can't start with a digit._discount— legal: a leading underscore is valid (by convention it suggests "internal use", as you'll see in module 5); for a normal variable,discountwould do.for— invalid: 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")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.95Python 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
- 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
