AlgoMaster Logo

Variables

High Priority20 min readUpdated June 6, 2026
Listen to this chapter
Unlock Audio

A variable in Python is a name that refers to a value. The mental model most beginners pick up first, that a variable is a labeled box holding a value, will lead you wrong in Python. Names point to objects, and assignment rebinds the name. This lesson walks through what = actually does, how multiple names can share a single object, and the small set of syntactic patterns you'll use every day.

Names, Not Boxes

When you write price = 19.99, three things happen, in order:

  1. Python evaluates the right-hand side and creates a float object with the value 19.99.
  2. Python looks up the name price in the current scope.
  3. Python binds the name price to that object.

The variable isn't a container that stores 19.99. It's a label attached to the object. If you reassign price, the label moves to a different object. The old object stays put until nothing else refers to it, at which point Python's garbage collector reclaims its memory.

The second assignment didn't change the original 19.99 object. It created a new float for 24.99 and pointed price at it. The old 19.99 is now unreachable and eligible for cleanup.

You can see this with the built-in id() function, which returns a unique integer identifier for an object (in CPython, this is the object's memory address).

Different ids confirm these are two distinct objects. The name price was rebound, the float wasn't mutated.

Here's the picture in your head:

The name moved. The old object didn't change, it just lost its only reference.

Two Names, One Object

A name binding is a one-way arrow from name to object. Nothing stops you from pointing two names at the same object.

The ids match. The is operator checks whether two names refer to the same object, and it returns True here. Now look at what that means in practice.

You appended through items, but the change shows up under cart too. There's only one list. Two names point to it. Mutating it through either name affects both views.

This trips up everyone at first. For now, hold onto the rule: assignment doesn't copy. It shares.

Two arrows, one object. That's the model.

Rebinding vs Mutating

These two operations look similar but behave very differently:

Reassigning cart rebound the name to a brand-new list object. The name items still points to the original list, which wasn't touched. Contrast this with the previous example, where calling items.append(...) mutated the shared object so both names saw the change.

The rule of thumb: assignment to a bare name (cart = ...) rebinds that name only. Method calls or item assignment that change the object's contents (cart.append(...), cart[0] = ...) mutate the object, and every name pointing at it sees the change.

OperationEffectVisible through other names?
x = something_newRebinds xNo, other names still point to the old object
x.append(item)Mutates the objectYes, all names share the change
x[0] = itemMutates the objectYes, all names share the change
x = x + [item]Rebinds x to a new listNo, this builds a new list and rebinds

The last row is worth noting. x = x + [item] looks like a mutation but isn't. It builds a fresh list by concatenating, then rebinds x. Other names keep their old reference.

Multiple Assignment

Python lets you assign to several names at once. The right-hand side is evaluated first, then the values are unpacked into the names on the left.

This is called tuple unpacking, because the right-hand side is a tuple ("Aisha", "aisha@example.com", 28). The number of names on the left must match the number of values on the right, or Python raises ValueError.

You can also chain assignments to give several names the same value:

All three names now point to the same 0 object. Since 0 is an immutable integer, this is safe. With a mutable object, chained assignment shares the same object across all names, which is usually not what you want.

Both names refer to the same list, so appending through cart_a shows up in cart_b. If you wanted two independent empty carts, write cart_a, cart_b = [], [] instead.

Swapping Values

The unpacking pattern gives you a clean way to swap two variables without a temporary. In many languages this requires a third variable; in Python it's one line.

The right-hand side second_item, first_item is evaluated to a tuple first, then unpacked into the two names on the left. The swap happens through the tuple, not through some special swap operator.

This works for any number of variables and for elements of a list. A common use is reordering the top of a wishlist:

Augmented Assignment

Augmented assignment combines an operation with a rebind. The form x += y is shorthand for "compute x + y and bind the result to x."

Every numeric and bitwise operator has an augmented form:

OperatorEquivalent toUse case
+=x = x + yAdd to running total
-=x = x - yDeduct stock, decrement quantity
*=x = x * yApply a multiplier
/=x = x / yDivide and rebind
//=x = x // yInteger division
%=x = x % yModulo
**=x = x ** yPower

For numbers and strings (which are immutable), augmented assignment always rebinds the name to a new object. For mutable objects like lists, the picture is slightly different.

list += other_list is equivalent to list.extend(other_list). It mutates the existing list in place rather than creating a new one. Both names still point at the same list, and both see the new element. Compare that with regular +.

The + operator builds a new list, and the assignment rebinds only cart. The original list, which items still points to, is unchanged. So += and = ... + are not always interchangeable for mutable types.

Dynamic Typing

Python is dynamically typed, which means the type of a value is associated with the value itself, not with the name. You don't declare int price or string name ahead of time. The same name can hold values of different types at different times.

The variable order_id was rebound three times, each time to a value of a different type. Python is fine with this. The downside is that you find out about type mismatches at runtime, not while writing the code.

quantity is a string, not a number, so multiplying by 2.99 fails. Python doesn't auto-convert strings to floats.

The freedom is nice, the responsibility is yours. Bigger codebases often use optional type hints (quantity: int = 5) to document intent and let tools catch type errors before runtime. For now, just know that Python won't complain if you rebind a name to a different type, but the operations you later try on it might.

Identifier Rules

A variable name in Python (also called an identifier) follows a few rules:

  • Starts with a letter (a-z, A-Z) or underscore (_). It cannot start with a digit.
  • The rest can be letters, digits, or underscores. No spaces, no hyphens, no punctuation.
  • Names are case-sensitive: total, Total, and TOTAL are three different names.
  • You can't use a reserved keyword as a name. The full list lives in the keyword module.

Trying to use any of these as a name raises SyntaxError:

The fix is to pick a different name, like category or class_name. By convention, when you need a name that collides with a keyword or built-in (such as class or type), Python developers add a trailing underscore: class_, type_. This is documented in the PEP 8 style guide.

A few names that are technically valid but a bad idea:

These overwrite built-in functions or types inside the current scope. After running them, list(...) and print(...) no longer behave as expected. Avoid using names that shadow built-ins.

Deleting a Name with del

The del statement removes a name binding. After del, the name no longer exists in the current scope, and trying to use it raises NameError.

del doesn't directly destroy the object, it just removes one reference to it. If other names still point to the same object, it stays alive.

The list still exists because items references it. Only the cart name was removed. When the last reference goes away, Python's garbage collector reclaims the memory.

del also works on items inside a collection, like del cart[0] to remove the first element of a list, or del scores["alice"] to remove a key from a dictionary.

In day-to-day code, you rarely need del for plain variables. Names go out of scope on their own when a function returns. The most common use is removing items from a collection or freeing a large object earlier than the function's end.

Common Mistakes

A short tour of the patterns that trip up new Python developers.

What's wrong with this code?

The first line runs before quantity and price are defined. Python evaluates quantity * price immediately and raises NameError because neither name exists yet. The fix is to assign the inputs first, then compute the result:

What's wrong with this code?

Customer_Name (with capitals) is a different name from customer_name. Python raises NameError: name 'Customer_Name' is not defined. Names are case-sensitive. Stick to one convention (lowercase with underscores, per PEP 8) and use it everywhere.

What's wrong with this code?

backup = cart did not copy the list. Both names point to the same list object, and cart.clear() mutates it in place. The "backup" is empty because there's only one list. If you wanted an independent copy: backup = cart.copy() or backup = cart[:].

Quiz

Variables Quiz

10 quizzes