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 (score1 is valid, 1score is not).
  • Names are case-sensitive — total and Total are 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 as total_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 for counter = 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:

  1. Python evaluates the right-hand side, x, by looking up the object it currently refers to. No new object is created.
  2. Python creates (or updates) the name y in the current namespace.
  3. Python makes y point at the same object that x points at. x and y are now two independent names sharing one object — x is y would be True.

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_case names (user_age, not ua or userAge) so the code reads clearly without extra comments.
  • Never shadow built-in names such as list, dict, str, type, or id.
  • 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_CASE names for module-level constants that should never be reassigned, such as MAX_RETRIES = 3.
  • When you need an independent copy of a mutable object, copy it explicitly with .copy() or copy.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, y in tight, obvious contexts.

Practice Exercises

  1. Create three variables — first_name, last_name, and birth_year — and use an f-string to print a sentence introducing yourself, including your current age calculated from birth_year.
  2. Given celsius = 23, write the conversion formula to create a new variable fahrenheit, then print both values. (Hint: fahrenheit = celsius * 9 / 5 + 32; expected output includes 73.4.)
  3. Create a list inventory = ["pen", "notebook", "eraser"], assign inventory_copy = inventory.copy(), remove one item from inventory_copy, and print both lists to confirm that inventory is 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 the is operator reveal whether two names share the same object.
  • Assignment never copies mutable objects like lists or dictionaries; use .copy() or copy.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.