Python Variables
A variable in Python is simply a name that points to a value stored in memory, so your code can refer to that value again without retyping it. Variables let a program remember things as it runs — a user’s name, a running total, the results of a calculation — and reuse or update them later. Unlike variables in languages such as Java or C, a Python variable has no fixed type of its own; it is just a label, and what it labels can change from one line to the next. Understanding this "name pointing at an object" model is the key to writing correct Python and avoiding some of the language’s most common surprises.
Overview: How Variables Really Work
In Python, every piece of data — a number, a string, a list, a function, even a class — exists as an object somewhere in memory. Assignment with the = operator does not copy a value into a fixed-size box the way it might in C. Instead, it makes a name in the current namespace refer to, or "bind to," an existing object. Python variables are best thought of as labeled arrows pointing at objects, not as boxes that contain values.
When Python executes age = 29, three things happen: it evaluates the right-hand side, producing an integer object 29 (small integers like this are cached and reused by CPython rather than recreated every time); it looks up or creates the name age in the current namespace; and it points that name at the object. Crucially, the type belongs to the object, not to the name — this is why Python is called dynamically typed. The same name can be bound to an integer on one line and a string two lines later, and Python will not complain.
Two built-in tools make this name/object relationship visible. type(x) reports the type of whatever object x currently refers to, and id(x) returns a unique integer identifying that object in memory (in CPython this is effectively the object’s memory address). When two names refer to the exact same object, the is operator returns True. The distinction between "equal value" (==) and "the same object" (is) becomes critical once mutable objects like lists and dictionaries are involved, because changing a mutable object through one name is visible through every other name bound to it.
Every name lives inside a namespace — a mapping of names to objects that Python maintains automatically. At the top level of a script, that is the module’s global namespace; inside a function, each call gets its own fresh local namespace. This is why a variable created inside a function normally disappears once the function returns, and why the same variable name can be reused safely in different functions without any conflict.
Syntax
The general form of a variable assignment is:
variable_name = value
| Part | Meaning |
|---|---|
variable_name |
The identifier you are creating or rebinding. |
= |
The assignment operator — binds the name to the object on the right. It is not mathematical equality. |
value |
Any expression: a literal, a calculation, a function call, another variable, and so on. |
Naming Rules
- Names may contain letters, digits, and underscores, but cannot start with a digit (
score1is valid,1scoreis not). - Names are case-sensitive —
totalandTotalare different variables. - Names cannot be one of Python’s reserved keywords (
class,for,if,return, and so on). - By convention (PEP 8), variable names use
snake_case: lowercase words separated by underscores, such astotal_price.
Python also supports several convenient assignment forms in a single statement:
x = 5
a, b, c = 1, 2, 3
p = q = 100
counter = 0
counter += 1
- Multiple assignment (
a, b, c = 1, 2, 3) unpacks a tuple of values into several names at once — the number of names must match the number of values. - Chained assignment (
p = q = 100) binds several names to the same object in one statement. - Augmented assignment (
counter += 1) is shorthand forcounter = counter + 1; Python offers+=,-=,*=,/=,//=,%=,**=, and the bitwise equivalents.
Examples
Example 1: Basic Assignment and Printing
name = "Ava"
age = 29
height_m = 1.68
is_student = False
print(f"{name} is {age} years old.")
print(f"Height in meters: {height_m}")
print(f"Currently a student: {is_student}")
Output:
Ava is 29 years old.
Height in meters: 1.68
Currently a student: False
Four variables are created, each bound to a different type of object: a string, an integer, a float, and a boolean. Notice that no type declarations are needed anywhere — Python figures out the type from the object each name is bound to, and the f-strings automatically convert each value to its readable text form.
Example 2: Reassignment and Dynamic Typing
x, y, z = 10, 20, 30
print(x, y, z)
x = "now I'm a string"
print(type(x))
count = total = 0
total += 5
count += 1
print(count, total)
Output:
10 20 30
<class 'str'>
1 5
The first line uses multiple assignment to create three variables in one statement. Then x, which was bound to the integer 10, is rebound to a completely different type of object — a string — and Python allows this without any error, because the name itself carries no type. Finally, count and total are chain-assigned to the same object (0) and then updated independently with augmented assignment.
Example 3: A Realistic Calculation
price_per_item = 4.50
quantity = 3
tax_rate = 0.08
subtotal = price_per_item * quantity
tax = subtotal * tax_rate
total = subtotal + tax
print(f"Subtotal: ${subtotal:.2f}")
print(f"Tax: ${tax:.2f}")
print(f"Total: ${total:.2f}")
a, b = 5, 10
a, b = b, a
print(f"a={a}, b={b}")
Output:
Subtotal: $13.50
Tax: $1.08
Total: $14.58
a=10, b=5
This example chains several variables together in a small calculation, the way you would in a real program: each variable’s value is derived from the ones before it. The last two lines show a classic Python idiom — swapping two variables using tuple unpacking, without needing a temporary third variable as you would in many other languages.
Under the Hood: References, Identity, and Mutability
Here is what actually happens, step by step, when Python runs an assignment statement such as y = x:
- Python evaluates the right-hand side,
x, by looking up the object it currently refers to. No new object is created. - Python creates (or updates) the name
yin the current namespace. - Python makes
ypoint at the same object thatxpoints at.xandyare now two independent names sharing one object —x is ywould beTrue.
For immutable objects — numbers, strings, tuples, booleans — this sharing is invisible, because nothing about the code can change the object itself; any operation that looks like a change (x += 1) actually creates a brand-new object and rebinds the name to it. But for mutable objects — lists, dictionaries, sets, and most custom class instances — the shared object really can be changed in place, and every name bound to it will see the change.
list_a = [1, 2, 3]
list_b = list_a
list_b.append(4)
print(list_a)
print(list_b)
print(list_a is list_b)
list_c = list_a.copy()
list_c.append(99)
print(list_a)
print(list_c)
Output:
[1, 2, 3, 4]
[1, 2, 3, 4]
True
[1, 2, 3, 4]
[1, 2, 3, 4, 99]
list_b = list_a does not create a second list; it creates a second name for the same list object, which is why list_a is list_b reports True and why appending through list_b is visible through list_a too. list_c = list_a.copy(), by contrast, builds a genuinely new list with the same contents, so changes to list_c no longer affect list_a. Keeping this reference model in mind — names point at objects, and mutable objects can be shared — explains almost every surprising bug involving variables in Python.
Common Mistakes
Mistake 1: Using a Variable Before It Is Assigned
Python executes code top to bottom, so a name must be assigned before it is read. Reading it earlier raises a NameError at runtime:
print(favorite_color)
favorite_color = "blue"
Because favorite_color does not exist yet on the line that tries to print it, Python raises NameError: name 'favorite_color' is not defined. The fix is simply to assign the variable before you use it — move the assignment above the print call.
Mistake 2: Shadowing a Built-in Name
Names like list, str, dict, id, and sum are built-in functions, not reserved keywords, so Python lets you reassign them — but doing so hides the built-in for the rest of that scope:
list = [1, 2, 3]
print(list)
names = list("hello")
The first two lines run fine and print [1, 2, 3], but by the third line list no longer refers to the built-in type — it refers to your list of numbers — so calling it like a function raises TypeError: 'list' object is not callable. Avoid this entirely by choosing a name that doesn’t collide with a built-in, such as numbers or score_list.
Mistake 3: Assuming Assignment Copies a Mutable Object
original_scores = [90, 85, 77]
backup_scores = original_scores # NOT a copy!
backup_scores.append(100)
print("original:", original_scores)
print("backup:", backup_scores)
Output:
original: [90, 85, 77, 100]
backup: [90, 85, 77, 100]
The programmer likely wanted an independent backup, but backup_scores = original_scores only creates a second name for the same list, so appending to one appears in both. The fix is to explicitly copy the list:
original_scores = [90, 85, 77]
backup_scores = original_scores.copy()
backup_scores.append(100)
print("original:", original_scores)
print("backup:", backup_scores)
Output:
original: [90, 85, 77]
backup: [90, 85, 77, 100]
Now the two lists are independent objects, and modifying one has no effect on the other. The same idea applies to dictionaries (.copy()) and, for nested structures, copy.deepcopy().
Best Practices
- Use descriptive
snake_casenames (user_age, notuaoruserAge) so the code reads clearly without extra comments. - Never shadow built-in names such as
list,dict,str,type, orid. - Give a variable its final value as close as possible to where it is created; avoid assigning a placeholder and mutating it many lines later.
- Use
UPPER_CASEnames for module-level constants that should never be reassigned, such asMAX_RETRIES = 3. - When you need an independent copy of a mutable object, copy it explicitly with
.copy()orcopy.deepcopy()rather than relying on plain assignment. - Prefer multiple assignment (
x, y = 1, 2) over several separate lines when the values are logically related. - Add type hints (
age: int = 29) in larger programs to document intent, even though Python does not enforce them at runtime. - Avoid single-letter names except for short-lived loop counters or well-known conventions like
i,x,yin tight, obvious contexts.
Practice Exercises
- Create three variables —
first_name,last_name, andbirth_year— and use an f-string to print a sentence introducing yourself, including your current age calculated frombirth_year. - Given
celsius = 23, write the conversion formula to create a new variablefahrenheit, then print both values. (Hint:fahrenheit = celsius * 9 / 5 + 32; expected output includes73.4.) - Create a list
inventory = ["pen", "notebook", "eraser"], assigninventory_copy = inventory.copy(), remove one item frominventory_copy, and print both lists to confirm thatinventoryis unaffected. Then repeat the exercise without.copy()and explain in a comment why the result differs.
Summary
- A Python variable is a name bound to an object in memory, not a fixed-size box holding a value directly.
- Types belong to objects, not names, which is why the same variable can be reassigned to a different type — Python is dynamically typed.
type(x)shows the type of the current object;id(x)and theisoperator reveal whether two names share the same object.- Assignment never copies mutable objects like lists or dictionaries; use
.copy()orcopy.deepcopy()when an independent copy is needed. - Multiple, chained, and augmented assignment let you write several related assignments concisely.
- Follow PEP 8 naming conventions (
snake_case, avoid shadowing built-ins) to keep code readable and bug-free.
