Python Basics: Variables, Types, and Control Flow
Why Python Variables Work Nothing Like Java: A Developer's First Day Rethinking the Basics
Python variables are labels that point at objects β not typed boxes.
π― System Scenario & Requirements
System Goal
Python variables are labels that point at objects β not typed boxes.
Actors & Components
- Application or API tier
- Core service under design
- Operator or SRE observer
Functional Requirements
- TLDR: Python variables are labels that point at objects β not typed boxes.
- The type lives with the object, not the variable.
- Master truthiness, f-strings, for / while loops, and the handful of pitfalls that trip up every developer coming from Java or JavaScript, and you'll write idiomatic Python from day one.
Constraints & Assumptions
- Every developer coming from a typed language will hit at least three of these in their first week.
- Mutable Default Arguments Are a Trap This is one of the most common Python bugs for newcomers: 2.
- The most dangerous failure mode for developers migrating from Java is the mutable default argument trap .
- def process(items=[]) β every call that doesn't pass items shares the same list object.
β‘ Execution Stages & Decision Trace
Stage 1 β The Label Model: How Python Actually Stores Variables in Memory
In Java, a variable is a named container with a fixed type .
When you write int x = 5 , you reserve a box labelled x that holds only integers.
Deep Dive
Scenario context
Python variables are labels that point at objects β not typed boxes. The type lives with the object, not the variable. Master truthiness, f-strings, for/while loops, and the handful of pitfalls that trip up every developer coming from Java or JavaScript, and you'll write idiomatic Python from day one.
Maria has five years of Java experience. She sits down to write her first Python script and produces this:β¦
This walkthrough focuses on Why Python Variables Work Nothing Like Java: A Developer's First Day Rethinking the Basics.
Before making a design choice, clarify:
- Maria has five years of Java experience.
- She sits down to write her first Python script and produces this: The first line throws a SyntaxError .
- She removes the type declarations: It runs.
Concepts in play: jvm-internals-and-garbage-collection, python-concurrency-nogil, database-indexing, tree-data-structures
Stage 1 β The Label Model: How Python Actually Stores Variables in Memory
- In Java, a variable is a named container with a fixed type .
- When you write int x = 5 , you reserve a box labelled x that holds only integers.
- The type is a property of the box.
- In Python, a variable is a label (reference) that points at an object .
x = 5 # The label 'x' points at the integer object 5
print(type(x)) # <class 'int'>
x = "hello" # The label 'x' now points at the string object "hello"
print(type(x)) # <class 'str'>
x = [1, 2, 3] # Now 'x' points at a list
print(type(x)) # <class 'list'>
a = 42
b = 42
print(id(a)) # e.g., 140234567890
print(id(b)) # same address β Python caches small integers!
print(a is b) # True β they point at the SAME object
a = 1000
b = 1000
print(a is b) # False β large integers are NOT cached; different objects
Observable outcome: In Python, a variable is a label (reference) that points at an object .
Stage 2 β Python's Built-in Types: int, float, str, bool, None and Their Surprising Behaviors
- Python has a compact set of built-in scalar types.
- Here are the six you'll reach for constantly β including the surprising behaviors of each.
- int β Arbitrary Precision by Default Python integers never overflow.
- There's no int vs long distinction.
big = 2 ** 100
print(big) # 1267650600228229401496703205376
print(type(big)) # <class 'int'>
print(0.1 + 0.2) # 0.30000000000000004 β classic float imprecision
print(0.1 + 0.2 == 0.3) # False!
# Use math.isclose() for float comparisons
import math
print(math.isclose(0.1 + 0.2, 0.3)) # True
Observable outcome: There's no int vs long distinction.
Stage 3 β Strings in Real Python Code: f-strings, Slicing, and the Five Methods You'll Use Every Day
- Strings in Python are rich, immutable sequences.
- Here's what you'll actually use in production code.
- f-strings: The Modern Standard (Python 3.6+) Forget % formatting and .format() .
- f-strings are the standard: String Slicing: [start:stop:step] Strings support Python's full slice syntax.
name = "Alice"
age = 30
score = 98.567
# Old ways (still valid, but verbose)
print("Hello, %s. You are %d years old." % (name, age))
print("Hello, {}. You are {} years old.".format(name, age))
# Modern: f-string
print(f"Hello, {name}. You are {age} years old.")
print(f"Score: {score:.2f}") # Format spec: 2 decimal places -> 98.57
print(f"Double age: {age * 2}") # Expressions are evaluated inline
text = "Python Programming"
print(text[0:6]) # "Python" β chars at index 0,1,2,3,4,5
print(text[7:]) # "Programming" β from index 7 to end
print(text[:6]) # "Python" β from start to index 5
print(text[-11:]) # "Programming" β last 11 characters
print(text[::2]) # "Pto rgamn" β every other character
print(text[::-1]) # "gnimmargorP nohtyP" β reversed string
Observable outcome: f-strings are the standard: String Slicing: [start:stop:step] Strings support Python's full slice syntax.
Stage 4 β Control Flow Visualized: if, elif, else, Ternary, and the match Statement
- Python uses indentation to define blocks.
- The colon at the end of if , elif , else , for , while , and def is mandatory.
- Ternary Expression: Inline if/else The match Statement (Python 3.10+) Python's match statement is pattern matching β far more powerful than a Java switch .
- It matches against values, types, sequences, and even attribute patterns: Short-Circuit Evaluation Python and and or are short-circuit operators β they stop evaluating as soon as the result is determined.
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"
print(f"Grade: {grade}") # Grade: B
age = 20
status = "adult" if age >= 18 else "minor"
print(status) # "adult"
Observable outcome: It matches against values, types, sequences, and even attribute patterns: Short-Circuit Evaluation Python and and or are short-circuit operators β they stop evaluating as soon as the result is determined.
Stage 5 β Loops and Functions: Progressive Examples from Range to First-Class Objects
- for Loops Over Iterables β Not Indices Python's for loop iterates over any iterable directly.
- You never need an index to traverse a list: range() for Numeric Sequences enumerate() β Index and Value Together Never write range(len(items)) to get an index.
- Use enumerate() : zip() β Iterate Two Lists in Lockstep while with break and continue The Loop-Else Clause: Underused, Often Surprising Python's for and while loops have an optional else clause that runs only if the looβ¦
- This is perfect for search patterns: The flow through loops and their branches is worth visualising.
fruits = ["apple", "banana", "cherry"]
# Java-style (works, but un-Pythonic)
for i in range(len(fruits)):
print(fruits[i])
# Pythonic
for fruit in fruits:
print(fruit)
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for i in range(1, 6): # 1, 2, 3, 4, 5
print(i)
for i in range(0, 10, 2): # 0, 2, 4, 6, 8 β step of 2
print(i)
Observable outcome: This is perfect for search patterns: The flow through loops and their branches is worth visualising.
Stage 6 β Python Type Tooling: How mypy and Type Hints Bring Discipline to Dynamic Code
- Python is dynamically typed, but that doesn't mean you have to fly blind.
- Since Python 3.5, you can add type hints β optional annotations that document expected types.
- They don't change runtime behavior, but they enable static analysis tools to catch bugs before you run the code.
- mypy is the leading open-source static type checker for Python.
# Without type hints β valid Python, but mypy can't help you here
def add(a, b):
return a + b
# With type hints β mypy can now verify callers
def add_typed(a: int, b: int) -> int:
return a + b
result: int = add_typed(3, 4) # OK
wrong = add_typed("hello", 4) # mypy error: Argument 1 to "add_typed" has incompatible type "str"; expected "int"
from typing import Optional, list
def find_user(user_id: int) -> Optional[str]:
"""Returns the username if found, None otherwise."""
users = {1: "Alice", 2: "Bob"}
return users.get(user_id)
# Runtime type checking with isinstance()
def process(value: int | str) -> str:
if isinstance(value, int):
return f"Number: {value}"
return f"Text: {value}"
print(process(42)) # "Number: 42"
print(process("hello")) # "Text: hello"
Observable outcome: mypy is the leading open-source static type checker for Python.
End-to-end outcome
After working through the stages, you should be able to explain:
- After watching many experienced developers learn Python, the same friction points appear every time: The label mental model takes 48 hours to actually land.
- Developers understand it intellectually in ten minutes, but then continue writing Java-style null checks and type assertions for days.
Alternatives considered
Decision Guide: Choosing the Right Python Control Flow Construct
Decision Guide: Choosing the Right Python Control Flow Construct
Failure and degraded modes
These are not edge cases. Every developer coming from a typed language will hit at least three of these in their first week.
- Detection: Every developer coming from a typed language will hit at least three of these in their first week.
- User or operator impact: Every developer coming from a typed language will hit at least three of these in their first week.
1. Mutable Default Arguments Are a Trap
- Detection: This is one of the most common Python bugs for newcomers:
- User or operator impact: This is one of the most common Python bugs for newcomers:
2. == vs is: Equality vs Identity
- Detection: == checks if two objects have the same value .
- User or operator impact: == checks if two objects have the same value .
3. Integer Division Always Returns float in Python 3
- Detection: 3. Integer Division Always Returns float in Python 3
- User or operator impact: 3. Integer Division Always Returns float in Python 3
Trade-offs & Failure Modes: Dynamic Typing at Scale
- Detection: The most dangerous failure mode for developers migrating from Java is the mutable default argument trap .
- User or operator impact: The most dangerous failure mode for developers migrating from Java is the mutable default argument trap .
