> "The question of whether a computer can think is no more interesting than the question of whether a submarine can swim."
Learning Objectives
- Create variables and assign values of different types (int, float, str, bool)
- Evaluate arithmetic, string, and comparison expressions
- Convert between types using type casting functions
- Use f-strings to format output with variables and format specifiers
- Explain how Python manages variables in memory (names as labels, not boxes)
In This Chapter
- Chapter Overview
- 3.1 What Are Variables?
- 3.2 Naming Variables
- 3.3 Data Types: int, float, str, bool
- 3.4 Arithmetic Expressions
- 3.5 String Expressions
- 3.6 Boolean Expressions
- 3.7 Type Casting
- 3.8 f-Strings: Formatted Output
- 3.9 Augmented Assignment
- 3.10 How Python Manages Memory
- 3.11 Project Checkpoint: TaskFlow v0.2
- Spaced Review
- 3.12 Chapter Summary
- What's Next
- Chapter 3 Exercises → exercises.md
- Chapter 3 Quiz → quiz.md
- Case Study: Building a Unit Converter → case-study-01.md
- Case Study: How a Simple Typo Cost a Company Millions → case-study-02.md
Chapter 3: Variables, Types, and Expressions: The Building Blocks
"The question of whether a computer can think is no more interesting than the question of whether a submarine can swim." — Edsger W. Dijkstra
Chapter Overview
In Chapter 2, you wrote programs that printed messages and took input from the user — but everything was fleeting. You couldn't hold onto a value, compute with it, or combine it with other values. That changes now.
Variables, types, and expressions are the fundamental building blocks of every program you'll ever write. A variable lets you give a name to a piece of data. A type tells Python what kind of data it is — a number? Text? A yes/no flag? An expression lets you combine values and variables into new values: adding numbers, joining strings, comparing scores to a threshold.
Here's what's different about this chapter from most CS1 textbooks: we're going to get the mental model right from the start. Most beginners think of a variable as a box that holds a value. That metaphor is intuitive but wrong — and it will mislead you later when things get more interesting. In Python, a variable is a name tag attached to an object in memory. Getting this right now saves you hours of confusion in Chapters 8, 14, and beyond.
By the end of this chapter, you'll have the vocabulary and tools to build programs that actually compute things — and you'll add timestamp-tracked tasks to your TaskFlow project.
In this chapter, you will learn to: - Create variables and assign values of different types (int, float, str, bool) - Evaluate arithmetic, string, and comparison expressions - Convert between types using type casting functions - Use f-strings to format output with variables and format specifiers - Explain how Python manages variables in memory (names as labels, not boxes)
🏃 Fast Track: If you've programmed in another language and just need the Python syntax, skim 3.1-3.2, focus on 3.8 (f-strings are Python-specific and excellent), and read 3.10 (Python's memory model differs from C/Java). Then do the project checkpoint.
🔬 Deep Dive: Section 3.10 on Python's memory model rewards careful reading. If you want to go further, try running
id()on various objects in the REPL and experiment with what happens when you rebind names.
3.1 What Are Variables?
Let's start with a program that has a problem:
print(95)
print(87)
print(91)
print("Average:", (95 + 87 + 91) / 3)
95
87
91
Average: 91.0
This works, but it's fragile. What if you need to change one of the scores? You'd have to change it in two places — once in the print() call and once in the average calculation. And what do those numbers even mean? Is 95 a temperature? A test score? A heart rate?
Variables solve both problems. A variable gives a meaningful name to a value so you can use it, reuse it, and modify it in one place.
score_1 = 95
score_2 = 87
score_3 = 91
average = (score_1 + score_2 + score_3) / 3
print(score_1)
print(score_2)
print(score_3)
print("Average:", average)
95
87
91
Average: 91.0
Now the code is self-documenting. Anyone reading it can tell these are scores, and if you need to change score_2, you change it in one place and the average updates automatically.
3.1.1 Assignment: The = Operator
The = sign in Python is the assignment operator. It does not mean "equals" in the mathematical sense. It means "make this name point to this value."
student_name = "Elena"
Read this as: "the name student_name now refers to the string object "Elena"."
Here's something that would be nonsense in math but makes perfect sense in Python:
counter = 0
counter = counter + 1
print(counter)
1
In math, counter = counter + 1 has no solution. In Python, it means: "Take the current value of counter (which is 0), add 1 to get 1, and make counter point to that new value."
3.1.2 The Name Tag Model
🚪 Threshold Concept: Variables as Name Tags, Not Boxes
Most beginners imagine a variable as a box that holds a value — like a physical container labeled
xwith the number 42 sitting inside it.This mental model is wrong. In Python, a variable is a name tag (or label, or sticky note) attached to an object that lives somewhere in memory.
Before this clicks: "The variable
xIS the number 42."After this clicks: "The variable
xPOINTS TO an object that happens to be 42 right now."Why does this matter? Because two name tags can point to the same object:
python a = 42 b = aIn the "box" model, this would mean copying 42 into a second box. But that's not what happens. Both
aandbare name tags attached to the same integer object42in memory. There's only one 42. Two names point to it.We'll prove this in section 3.10 using
id(). For now, just start building the habit of thinking of variables as name tags pointing to objects, not boxes holding values.
Here's a critical consequence of the name tag model:
a = 42
b = a # b now points to the same 42 that a points to
a = 99 # a gets a NEW name tag — it now points to 99
print(a) # 99
print(b) # still 42!
99
42
When we wrote a = 99, we didn't change the object 42. We moved the a name tag from the 42 object to a new 99 object. The b name tag is still stuck to 42, unaffected.
🧩 Productive Struggle: Before reading on, predict the output of this code. Write down your answer, then check it:
python x = 5 y = x x = 10 print(y)
Answer
yis5. When you wrotey = x, you madeypoint to the same object asx(which was 5). When you then wrotex = 10, you moved thexname tag to a new object (10). Theyname tag didn't move — it's still attached to 5.If you said 10, you were using the "box" model. In the box model,
y = xwould makeysomehow trackx. But name tags don't work that way. Each name independently points to an object.
3.2 Naming Variables
Python has rules about what you can name a variable, and conventions about what you should name it.
3.2.1 The Rules (Python enforces these)
- Names can contain letters, digits, and underscores:
score_1,total_count,x2 - Names cannot start with a digit:
1st_placeis illegal;first_placeis fine - Names are case-sensitive:
Score,score, andSCOREare three different variables - Names cannot be Python keywords (reserved words):
if,while,for,class,return,True,False,None, etc.
# Legal names
student_name = "Elena"
score_1 = 95
_internal = "private by convention"
totalCount = 42
# Illegal names — these cause SyntaxError
# 1st_place = "Gold" # starts with a digit
# my-variable = 10 # hyphens aren't allowed
# class = "Biology" # 'class' is a keyword
3.2.2 The Conventions (community standards you should follow)
Python developers follow a set of conventions documented in PEP 8, Python's official style guide:
- Use snake_case for variable names:
student_name, notstudentName - Use all caps for constants:
MAX_RETRIES = 3,PI = 3.14159 - Choose meaningful names:
average_scorebeatsa, andtemperature_celsiusbeatst - Keep names reasonably short but descriptive:
nis fine in a loop, butnumber_of_students_enrolled_this_semesteris too long
✅ Best Practice: A good variable name answers the question "what is this?" without needing a comment. If you find yourself writing
x = 95 # student's midterm score, just name itmidterm_scoreinstead.
Here's the grade calculator from Chapter 1's concept introduction, now implemented with meaningful variable names:
student_name = "Elena"
midterm_score = 87
final_score = 93
homework_average = 91
overall_average = (midterm_score + final_score + homework_average) / 3
print(f"{student_name}'s average: {overall_average}")
Elena's average: 90.33333333333333
(We'll clean up that long decimal in section 3.8 when we learn f-string formatting.)
3.3 Data Types: int, float, str, bool
Every value in Python has a type — a category that determines what kind of data it is and what operations you can perform on it. Python has many types, but you'll work with four fundamental ones constantly.
3.3.1 Integers (int)
An integer is a whole number — no decimal point. Positive, negative, or zero.
students = 35
temperature = -12
year = 2025
zero = 0
print(type(students))
print(type(temperature))
<class 'int'>
<class 'int'>
The type() function tells you what type a value is. You'll use it constantly while learning. Python integers have no maximum size — they can be as large as your computer's memory allows.
big_number = 10 ** 100 # 10 to the power of 100 — a googol
print(big_number)
print(type(big_number))
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
<class 'int'>
3.3.2 Floating-Point Numbers (float)
A float (short for floating-point number) is a number with a decimal point.
gpa = 3.85
pi = 3.14159
negative_temp = -40.0
exactly_one = 1.0 # this is a float, not an int!
print(type(gpa))
print(type(exactly_one))
<class 'float'>
<class 'float'>
⚠️ Pitfall: Floating-point arithmetic can produce surprising results:
python print(0.1 + 0.2)
0.30000000000000004This isn't a Python bug — it's a fundamental limitation of how computers represent decimal numbers in binary. For now, just be aware of it. When comparing floats, don't use
==; instead, check whether they're "close enough." We'll revisit this in later chapters.
3.3.3 Strings (str)
A string is a sequence of characters — text. You create strings with single quotes, double quotes, or triple quotes:
greeting = "Hello, world!"
name = 'Elena'
multiline = """This string
spans multiple
lines."""
print(type(greeting))
print(greeting)
print(multiline)
<class 'str'>
Hello, world!
This string
spans multiple
lines.
Use double quotes or single quotes — pick one style and be consistent. Triple quotes are useful for text that spans multiple lines.
💡 Intuition: Strings are for text. Even if the text happens to contain digits, it's still text:
```python age_as_string = "25" age_as_int = 25
print(type(age_as_string)) #
print(type(age_as_int)) # ``` The string
"25"and the integer25are completely different objects. You can do math with the integer. You can't do math with the string (at least, not the way you'd expect).
3.3.4 Booleans (bool)
A boolean is either True or False. That's it. Two possible values.
is_enrolled = True
has_prerequisite = False
print(type(is_enrolled))
print(is_enrolled)
<class 'bool'>
True
Note the capitalization: True and False, not true and false. Python is picky about this.
Booleans become essential in Chapter 4 when we start making decisions with if statements. For now, just know they exist and that comparison expressions produce them (section 3.6).
3.3.5 Type Comparison Table
| Type | Python Name | Example Values | What It Represents |
|---|---|---|---|
| Integer | int |
42, -7, 0, 10**100 |
Whole numbers |
| Float | float |
3.14, -0.001, 1.0 |
Numbers with decimal points |
| String | str |
"hello", 'CS 101', "" |
Text (sequence of characters) |
| Boolean | bool |
True, False |
Logical values (yes/no, on/off) |
🔄 Check Your Understanding (try to answer without scrolling up)
- What does
type(3.0)return?- Is
"42"the same type as42?- What are the only two possible values of a boolean?
Verify
<class 'float'>— the decimal point makes it a float, even though 3.0 is mathematically a whole number.- No.
"42"is astr(string), and42is anint(integer). They look similar but are fundamentally different types.TrueandFalse(capitalized).
3.4 Arithmetic Expressions
An expression is any combination of values, variables, and operators that Python can evaluate to produce a result. Arithmetic expressions work with numbers.
3.4.1 The Arithmetic Operators
a = 17
b = 5
print("Addition: ", a + b) # 22
print("Subtraction: ", a - b) # 12
print("Multiplication: ", a * b) # 85
print("Division: ", a / b) # 3.4
print("Floor division: ", a // b) # 3
print("Modulo: ", a % b) # 2
print("Exponentiation: ", a ** b) # 1419857
Addition: 22
Subtraction: 12
Multiplication: 85
Division: 3.4
Floor division: 3
Modulo: 2
Exponentiation: 1419857
| Operator | Name | Example | Result | Notes |
|---|---|---|---|---|
+ |
Addition | 17 + 5 |
22 |
|
- |
Subtraction | 17 - 5 |
12 |
|
* |
Multiplication | 17 * 5 |
85 |
|
/ |
Division | 17 / 5 |
3.4 |
Always returns a float |
// |
Floor division | 17 // 5 |
3 |
Rounds down to nearest integer |
% |
Modulo | 17 % 5 |
2 |
Remainder after division |
** |
Exponentiation | 2 ** 10 |
1024 |
Raises to a power |
3.4.2 Division: / vs //
This trips up beginners. Python has two division operators:
/is true division — it always gives you a float, even when the result is a whole number.//is floor division — it drops the decimal part (rounds toward negative infinity).
print(10 / 3) # 3.3333333333333335
print(10 // 3) # 3
print(10 / 2) # 5.0 — still a float!
print(10 // 2) # 5 — an integer
3.3333333333333335
3
5.0
5
The modulo operator % gives you the remainder, which pairs naturally with floor division:
minutes = 135
hours = minutes // 60
remaining_minutes = minutes % 60
print(f"{minutes} minutes = {hours} hours and {remaining_minutes} minutes")
135 minutes = 2 hours and 15 minutes
3.4.3 Operator Precedence
Python follows standard mathematical order of operations (PEMDAS/BODMAS):
**(exponentiation) — highest- Unary
-(negation) *,/,//,%(multiplication, division, floor division, modulo)+,-(addition, subtraction) — lowest
result = 2 + 3 * 4 # 14, not 20 — multiplication first
print(result)
result = (2 + 3) * 4 # 20 — parentheses override precedence
print(result)
result = 2 ** 3 ** 2 # 512, not 64 — ** is right-associative: 2 ** (3 ** 2) = 2 ** 9
print(result)
14
20
512
✅ Best Practice: When in doubt, use parentheses.
(2 + 3) * 4is clearer than relying on precedence rules, even if you know them by heart. Readability beats cleverness.
3.4.4 The Grade Calculator with Expressions
Let's build on our grade calculator. Elena's course has a weighted grading scheme:
# Elena's course grades
student_name = "Elena"
homework = 91.5 # 30% of final grade
midterm = 87.0 # 30% of final grade
final_exam = 93.0 # 40% of final grade
# Weighted average
weighted_average = (homework * 0.30) + (midterm * 0.30) + (final_exam * 0.40)
print(f"{student_name}'s weighted average: {weighted_average}")
Elena's weighted average: 90.75
🔄 Check Your Understanding
Without running the code, what does each expression evaluate to?
15 // 415 % 42 ** 410 / 5
Verify
3— floor division drops the remainder3— the remainder of 15 divided by 416— 2 raised to the 4th power2.0— true division always returns a float
3.5 String Expressions
Strings have their own operators. You can't divide a string by 2, but you can do other useful things.
3.5.1 Concatenation with +
The + operator joins strings together (concatenation):
first_name = "Elena"
last_name = "Vasquez"
full_name = first_name + " " + last_name
print(full_name)
Elena Vasquez
Notice we had to add a space " " between the names — + doesn't add spaces automatically.
3.5.2 Repetition with *
The * operator repeats a string:
line = "-" * 40
print(line)
print("REPORT HEADER")
print(line)
----------------------------------------
REPORT HEADER
----------------------------------------
This is handy for creating visual separators in text output.
3.5.3 String Length with len()
The len() function returns the number of characters in a string:
message = "Hello, world!"
print(len(message))
empty = ""
print(len(empty))
13
0
Spaces, punctuation, and special characters all count as characters.
# Practical use: validating input length
password = "abc123"
print(f"Password length: {len(password)}")
print(f"Long enough? {len(password) >= 8}")
Password length: 6
Long enough? False
⚠️ Pitfall: You can't use
+to concatenate a string with a number. Python won't guess what you mean:
python age = 20 message = "I am " + age + " years old" # TypeError!This produces a
TypeErrorbecause Python doesn't know if you want to add numbers or join text. You must explicitly convert:"I am " + str(age) + " years old". Or better yet, use an f-string (section 3.8).
3.6 Boolean Expressions
Boolean expressions use comparison operators to produce True or False. These become essential in Chapter 4 for making decisions, but you need to understand them now as a type.
3.6.1 Comparison Operators
score = 87
print(score == 90) # Equal to
print(score != 90) # Not equal to
print(score > 80) # Greater than
print(score < 80) # Less than
print(score >= 87) # Greater than or equal to
print(score <= 90) # Less than or equal to
False
True
True
False
True
True
| Operator | Meaning | Example | Result |
|---|---|---|---|
== |
Equal to | 5 == 5 |
True |
!= |
Not equal to | 5 != 3 |
True |
> |
Greater than | 5 > 3 |
True |
< |
Less than | 5 < 3 |
False |
>= |
Greater than or equal to | 5 >= 5 |
True |
<= |
Less than or equal to | 5 <= 3 |
False |
⚠️ Pitfall:
=is assignment.==is comparison. Mixing them up is one of the most common beginner mistakes:
python x = 5 # Assignment: make x point to 5 x == 5 # Comparison: does x equal 5? (True)If you write
if x = 5:instead ofif x == 5:, Python will give you aSyntaxError. This is actually Python being helpful — some other languages would silently do the wrong thing.
3.6.2 Comparing Strings
You can compare strings too. Python compares them character by character using Unicode (alphabetical) ordering:
print("apple" < "banana") # True — 'a' comes before 'b'
print("apple" == "Apple") # False — case matters!
print("abc" < "abd") # True — differs at 3rd character: 'c' < 'd'
True
False
True
🔄 Check Your Understanding
What does each expression evaluate to?
10 == 10.0"hello" != "Hello"True == 1
Verify
True— Python considers 10 and 10.0 equal (int and float comparison works).True— Python is case-sensitive; lowercase 'h' is not the same as uppercase 'H'.True— in Python,Trueis equivalent to1andFalseto0. This is becauseboolis actually a subclass ofint. Useful to know but rarely something you should rely on in your code.
3.7 Type Casting
Sometimes you need to convert a value from one type to another. This is called type casting (or type conversion). Python provides built-in functions for this.
3.7.1 The Casting Functions
# int() — converts to integer
print(int(3.7)) # 3 — truncates, does NOT round
print(int("42")) # 42 — parses the string as a number
print(int(True)) # 1
# float() — converts to float
print(float(42)) # 42.0
print(float("3.14")) # 3.14
print(float(False)) # 0.0
# str() — converts to string
print(str(42)) # "42"
print(str(3.14)) # "3.14"
print(str(True)) # "True"
# bool() — converts to boolean
print(bool(1)) # True
print(bool(0)) # False
print(bool("hello")) # True
print(bool("")) # False
3
42
1
42.0
3.14
0.0
42
3.14
True
True
False
True
False
3.7.2 When You Actually Need Type Casting
The most common case: input() always returns a string, even if the user types a number.
user_input = input("Enter your age: ")
print(type(user_input)) # <class 'str'> — always!
age = int(user_input) # Convert to integer
print(type(age)) # <class 'int'>
print(f"Next year you'll be {age + 1}")
Enter your age: 20
<class 'str'>
<class 'int'>
Next year you'll be 21
Without the int() conversion, trying to do user_input + 1 would crash with a TypeError.
3.7.3 Type Casting Gotchas
Not every conversion works. Python will tell you when it can't do what you're asking:
# These will crash
# int("hello") # ValueError: invalid literal for int()
# int("3.14") # ValueError: can't go str → int if string has a decimal
# float("abc") # ValueError: could not convert string to float
🐛 Debugging Walkthrough: The TypeError
Elena is building a simple script and writes:
python name = "Elena" score = 95 message = "Student " + name + " scored " + score + " points." print(message)She gets this error:
Traceback (most recent call last): File "report.py", line 3, in <module> message = "Student " + name + " scored " + score + " points." ~~~~~~^~~~~~~~~~~ TypeError: can only concatenate str (not "int") to strWhat happened: Python tried to concatenate (join) the string
" scored "with the integer95. Python doesn't auto-convert; it raises aTypeErrorinstead.The fix: Convert
scoreto a string, or (better) use an f-string:```python
Fix 1: explicit conversion
message = "Student " + name + " scored " + str(score) + " points."
Fix 2: f-string (recommended — covered in section 3.8)
message = f"Student {name} scored {score} points." ```
The lesson: Python's
TypeErroris not a failure — it's the language telling you exactly what's wrong. The error message says "can only concatenate str (not int) to str." Read it carefully and you'll know the fix. This is a theme we'll return to throughout the book: errors are not failures, they are information.
3.7.4 The bool() Truthiness Rules
Python has a consistent rule for what's "truthy" (converts to True) and what's "falsy" (converts to False):
| Value | bool() Result |
Rule |
|---|---|---|
0 |
False |
Zero is falsy |
0.0 |
False |
Zero is falsy |
"" (empty string) |
False |
Empty is falsy |
None |
False |
None is falsy |
| Any non-zero number | True |
Non-zero is truthy |
| Any non-empty string | True |
Non-empty is truthy |
print(bool(42)) # True
print(bool(-1)) # True
print(bool(0)) # False
print(bool("hi")) # True
print(bool("")) # False
print(bool(" ")) # True — a space is not empty!
True
True
False
True
False
True
This will matter a lot when we start writing if statements in Chapter 4.
3.8 f-Strings: Formatted Output
You've already seen f-strings a few times. Let's learn them properly. An f-string (formatted string literal) starts with f before the opening quote and lets you embed expressions directly inside curly braces {}.
3.8.1 Basic f-String Usage
name = "Elena"
score = 90.75
# Without f-strings (clunky)
print("Student: " + name + ", Score: " + str(score))
# With f-strings (clean)
print(f"Student: {name}, Score: {score}")
Student: Elena, Score: 90.75
Student: Elena, Score: 90.75
You can put any valid Python expression inside the curly braces:
a = 15
b = 4
print(f"{a} divided by {b} is {a / b}")
print(f"{a} modulo {b} is {a % b}")
print(f"The length of 'hello' is {len('hello')}")
15 divided by 4 is 3.75
15 modulo 4 is 3
The length of 'hello' is 5
3.8.2 Format Specifiers
You can control how values are displayed using format specifiers after a colon inside the braces:
average = 90.33333333333333
# Round to 2 decimal places
print(f"Average: {average:.2f}")
# Round to 1 decimal place
print(f"Average: {average:.1f}")
# Round to 0 decimal places (still shows as float)
print(f"Average: {average:.0f}")
Average: 90.33
Average: 90.3
Average: 90
More format specifiers you'll find useful:
price = 1234.5
percentage = 0.857
# Comma separator for thousands
print(f"Revenue: ${price:,.2f}")
# Percentage formatting
print(f"Accuracy: {percentage:.1%}")
# Padding and alignment
print(f"{'Name':<15}{'Score':>10}") # left-align, right-align
print(f"{'Elena':<15}{90.75:>10.1f}")
print(f"{'Marcus':<15}{87.00:>10.1f}")
Revenue: $1,234.50
Accuracy: 85.7%
Name Score
Elena 90.8
Marcus 87.0
| Specifier | Meaning | Example | Output |
|---|---|---|---|
.2f |
2 decimal places | f"{3.14159:.2f}" |
3.14 |
.1% |
Percentage, 1 decimal | f"{0.857:.1%}" |
85.7% |
, |
Thousands separator | f"{1000000:,}" |
1,000,000 |
<15 |
Left-align in 15 chars | f"{'hi':<15}" |
hi |
>10 |
Right-align in 10 chars | f"{42:>10}" |
42 |
💡 Intuition: f-strings are one of Python's best features. They replaced three older formatting methods (%, .format(), and string concatenation), and they're faster and more readable than all of them. Get comfortable with f-strings now — you'll use them in virtually every program you write.
Now let's fix Elena's grade calculator with proper formatting:
student_name = "Elena"
homework = 91.5
midterm = 87.0
final_exam = 93.0
weighted_average = (homework * 0.30) + (midterm * 0.30) + (final_exam * 0.40)
print(f"{'='*40}")
print(f" Grade Report: {student_name}")
print(f"{'='*40}")
print(f" {'Homework:':<20}{homework:>8.1f}")
print(f" {'Midterm:':<20}{midterm:>8.1f}")
print(f" {'Final Exam:':<20}{final_exam:>8.1f}")
print(f"{'─'*40}")
print(f" {'Weighted Average:':<20}{weighted_average:>8.2f}")
print(f"{'='*40}")
========================================
Grade Report: Elena
========================================
Homework: 91.5
Midterm: 87.0
Final Exam: 93.0
────────────────────────────────────────
Weighted Average: 90.75
========================================
3.9 Augmented Assignment
When you need to update a variable based on its current value, Python provides shorthand operators called augmented assignment operators:
score = 100
# Long form
score = score - 10
print(score) # 90
# Augmented assignment (same thing, more concise)
score -= 10
print(score) # 80
90
80
Here's the full set:
| Long Form | Augmented | Example | Result (if x = 10) |
|---|---|---|---|
x = x + 5 |
x += 5 |
x += 5 |
15 |
x = x - 3 |
x -= 3 |
x -= 3 |
7 |
x = x * 2 |
x *= 2 |
x *= 2 |
20 |
x = x / 4 |
x /= 4 |
x /= 4 |
2.5 |
x = x // 3 |
x //= 3 |
x //= 3 |
3 |
x = x % 3 |
x %= 3 |
x %= 3 |
1 |
x = x ** 2 |
x **= 2 |
x **= 2 |
100 |
Augmented assignment also works with strings:
message = "Hello"
message += ", world!"
print(message)
Hello, world!
You'll use += constantly — it's the most common one by far. Running totals, building strings piece by piece, counting things.
# Running total — summing up scores
total = 0
total += 95
total += 87
total += 91
count = 3
print(f"Total: {total}")
print(f"Average: {total / count:.1f}")
Total: 273
Average: 91.0
3.10 How Python Manages Memory
Let's return to the name tag model from section 3.1 and prove it with actual code. Python provides the id() function, which returns the memory address of an object (think of it as the object's unique ID).
3.10.1 Two Names, One Object
a = 42
b = a
print(f"a = {a}, id(a) = {id(a)}")
print(f"b = {b}, id(b) = {id(b)}")
print(f"Same object? {id(a) == id(b)}")
a = 42, id(a) = 140735123456320
b = 42, id(b) = 140735123456320
Same object? True
(Your actual numbers will be different, but id(a) and id(b) will match.)
Here's what's happening in memory:
Name Tags Memory
┌───┐ ┌──────────┐
│ a │ ──────────────▶│ 42 │
└───┘ ┌───▶│ (int) │
┌───┐ │ └──────────┘
│ b │ ──────────┘
└───┘
Both a and b are name tags pointing to the same integer object 42 in memory. There's only one 42.
3.10.2 Rebinding a Name
Now watch what happens when we reassign a:
a = 42
b = a
print(f"Before: id(a) = {id(a)}, id(b) = {id(b)}")
a = 99 # Rebind a to a new object
print(f"After: id(a) = {id(a)}, id(b) = {id(b)}")
print(f"a = {a}, b = {b}")
Before: id(a) = 140735123456320, id(b) = 140735123456320
After: id(a) = 140735123458144, id(b) = 140735123456320
a = 99, b = 42
After the reassignment, a and b have different id() values. They point to different objects:
Name Tags Memory
┌───┐ ┌──────────┐
│ a │ ──────────────▶│ 99 │
└───┘ │ (int) │
└──────────┘
┌───┐ ┌──────────┐
│ b │ ──────────────▶│ 42 │
└───┘ │ (int) │
└──────────┘
The b name tag didn't move. Reassigning a only moved the a name tag. This is the name tag model in action.
🔬 Deep Dive: Python caches small integers (typically -5 to 256) for performance, so
id(42)will always return the same address. But this is an implementation detail — don't write code that depends on it. The name tag model is the correct way to think about it regardless.
3.10.3 Why This Matters
With integers, floats, strings, and booleans, the name tag model and the box model happen to predict the same behavior. But in Chapter 8, when you work with lists (which are mutable — they can be changed in place), the distinction becomes critical:
# Preview of why the name tag model matters (Chapter 8)
list_a = [1, 2, 3]
list_b = list_a # Both names point to the SAME list
list_a.append(4) # Modify the list through name tag a
print(list_b) # [1, 2, 3, 4] — surprise! b sees the change too
If you were using the box model, you'd expect list_b to still be [1, 2, 3]. But both names point to the same object, so changes through one name are visible through the other. Getting the name tag model right now saves you from this confusion later.
🔗 Connection to Computational Thinking: The name tag model is an example of abstraction — one of the four pillars from Chapter 1. Python abstracts away the details of memory management (you never manually allocate or free memory), but understanding the concept of names pointing to objects helps you predict how your programs will behave. This is exactly the kind of "thinking behind the code" that separates beginners from confident programmers.
3.11 Project Checkpoint: TaskFlow v0.2
📐 Project Checkpoint: TaskFlow v0.2 — Add a Single Task with Timestamp
In Chapter 2, TaskFlow v0.1 greeted the user. Now we'll add a real feature: the user can enter a task name, and the program stores it with a timestamp showing when it was created.
Here's the full code for TaskFlow v0.2:
# taskflow_v02.py — TaskFlow v0.2: Add a task with timestamp
from datetime import datetime
# Greet the user (carried over from v0.1)
print("=" * 40)
print(" Welcome to TaskFlow v0.2")
print("=" * 40)
user_name = input("What's your name? ")
print(f"\nHello, {user_name}! Let's add a task.\n")
# Get task details
task_name = input("Enter a task: ")
task_created = datetime.now()
# Format the timestamp
timestamp_str = task_created.strftime("%Y-%m-%d %H:%M:%S")
# Display the task
print(f"\n{'─' * 40}")
print(f" Task added successfully!")
print(f"{'─' * 40}")
print(f" Task: {task_name}")
print(f" Added: {timestamp_str}")
print(f" Status: Pending")
print(f"{'─' * 40}")
# Show variable types for learning purposes
print(f"\n📊 Behind the scenes:")
print(f" task_name is a {type(task_name).__name__}: '{task_name}'")
print(f" task_created is a {type(task_created).__name__}: {task_created}")
print(f" timestamp_str is a {type(timestamp_str).__name__}: '{timestamp_str}'")
========================================
Welcome to TaskFlow v0.2
========================================
What's your name? Elena
Hello, Elena! Let's add a task.
Enter a task: Finish Ch3 exercises
────────────────────────────────────────
Task added successfully!
────────────────────────────────────────
Task: Finish Ch3 exercises
Added: 2025-09-15 14:32:07
Status: Pending
────────────────────────────────────────
📊 Behind the scenes:
task_name is a str: 'Finish Ch3 exercises'
task_created is a datetime: 2025-09-15 14:32:07.123456
timestamp_str is a str: '2025-09-15 14:32:07'
What's new in v0.2:
- Variables to store data (
user_name,task_name,task_created) - Type awareness —
datetime.now()returns a datetime object, which we convert to a formatted string - f-strings for clean output formatting
- String repetition (
"=" * 40) for visual structure
What's coming in v0.3 (Chapter 4): We'll add task priority (high/medium/low) using conditionals, and validate user input so the program handles unexpected entries gracefully.
🔄 Check Your Understanding
- What type is
task_name? What type istask_created?- Why do we call
task_created.strftime(...)instead of just printingtask_createddirectly?- What would happen if we wrote
print("Task: " + task_name + ", Added: " + task_created)without convertingtask_createdto a string?
Verify
task_nameis astr(frominput()).task_createdis adatetimeobject (fromdatetime.now()).strftime()lets us control the format — we get "2025-09-15 14:32:07" instead of "2025-09-15 14:32:07.123456" (with microseconds). It's also type casting in disguise: converting a datetime to a string in a specific format.- You'd get a
TypeError— you can't concatenate a string with a datetime object. You'd needstr(task_created)or an f-string.
Spaced Review
🔄 Retrieval Practice — Reaching Back
From Chapter 1 (Computational Thinking): Without looking back, list the four pillars of computational thinking and give a one-sentence definition of each.
Verify
- Decomposition: Breaking a complex problem into smaller, manageable pieces.
- Pattern Recognition: Spotting similarities between new problems and ones you've solved before.
- Abstraction: Ignoring irrelevant details to focus on what matters.
- Algorithm Design: Creating a precise, step-by-step procedure to solve a problem.
Bridge Question (connecting Ch 1 to Ch 3): In this chapter, you learned that variables are an implementation of abstraction — they let you give a meaningful name to a value and use it without caring about where it lives in memory. Identify one other example of abstraction from this chapter. (Hint: think about f-strings.)
Verify
f-strings are an abstraction over string formatting. You write
f"Score: {score:.2f}"and Python handles the conversion, rounding, and insertion for you. You don't need to know how Python internally converts a float to a two-decimal-place string representation — the f-string hides that complexity behind a simple{expression:format}syntax.
3.12 Chapter Summary
Key Concepts
- Variables are name tags (labels) that point to objects in memory — not boxes that hold values.
- Assignment (
=) binds a name to an object. Reassignment moves the name tag; it doesn't change the old object. - Python has four fundamental types:
int,float,str, andbool. - Expressions combine values, variables, and operators to produce results.
- Type casting (
int(),float(),str(),bool()) converts between types explicitly. - f-strings are the modern, readable way to format output with embedded expressions.
- Augmented assignment (
+=,-=, etc.) provides concise syntax for updating variables. type()reveals what type a value is;id()reveals where it lives in memory.
Key Terms Summary
| Term | Definition |
|---|---|
| Variable | A name that refers to an object stored in memory |
| Assignment | Binding a name to a value using the = operator |
Integer (int) |
A whole number: positive, negative, or zero |
Float (float) |
A number with a decimal point |
String (str) |
A sequence of characters (text) |
Boolean (bool) |
A logical value: True or False |
| Expression | A combination of values, variables, and operators that evaluates to a result |
| Operator | A symbol that performs an operation (e.g., +, -, *, ==) |
| Type casting | Converting a value from one type to another (e.g., int("42")) |
| f-string | A formatted string literal (f"...") that embeds expressions inside {} |
type() |
A built-in function that returns the type of a value |
| Augmented assignment | Shorthand operators like += and -= that update a variable in place |
What You Should Be Able to Do
- [ ] Create variables with meaningful names following PEP 8 conventions
- [ ] Identify and use all four fundamental types: int, float, str, bool
- [ ] Write arithmetic expressions and predict the result using operator precedence
- [ ] Convert between types using int(), float(), str(), bool()
- [ ] Format output with f-strings, including format specifiers for decimal places
- [ ] Explain the name tag model: two variables can point to the same object
- [ ] Use
type()andid()to inspect values at runtime
What's Next
In Chapter 4: Making Decisions: Conditionals and Boolean Logic, you'll learn how to make your programs choose between different paths based on conditions. The boolean expressions you learned in section 3.6 become the building blocks for if, elif, and else statements — the decision-making machinery of every program. You'll also meet Python's match/case statement and build TaskFlow v0.3, which adds task priorities and input validation.
The combination of variables (this chapter) and conditionals (next chapter) is where programs start to feel intelligent. They stop doing the same thing every time and start responding to data.
Before moving on, complete the exercises and quiz to solidify your understanding.