Technology Variables and Values | Python Tutorial

Learn with diagrams, code, systems and practical examples.

</>
TutorialsPythonVariables and Values
Core Python · Chapter 4

Variables and Values

Store values with meaningful names and update them safely.

Why this matters

Variables let your program remember information so later steps can use it.

Start with the idea

A variable name refers to a value. The equals sign assigns the value on the right to the name on the left. Choose names that communicate meaning rather than position.

IdeaUnderstand the purpose
CodeRun the smallest example
PracticeChange it yourself
SubjectVision learning pattern: understand the idea before memorizing syntax.

Small working example

Run this example first. Do not change several things at once; confirm the basic behavior, then experiment.

Python
customer_name = "Maya"
item_count = 3
item_count = item_count + 1
print(customer_name, item_count)
Expected output
Maya 4
What to notice

The example is intentionally small. Focus on the chapter’s main idea before adding extra syntax or framework code.

Common beginner mistake

Mistake

Reading x = x + 1 as a mathematical equation.

Better approach

Read assignment from right to left: calculate x + 1 first, then store that new value under x.

Quick recap

  • Variables are names for values.
  • Assignment updates what a name refers to.
  • Clear names make code easier to understand.

Try it yourself

These are deliberately small. If you can complete them without copying the example, you are ready to continue.

  1. Create variables for a product name and price.
  2. Increase a quantity by two and print the result.

Prepared deep chapter · Core Python

Variables are names that point to values

Imagine you are building a tiny shopping program. It needs to remember a customer name, how many items are in the cart, and whether the order is ready. You could write those values directly everywhere, but the program would become difficult to read and difficult to change. A variable gives a useful name to a value so later code can use that name.

The most important beginner idea is simple: in Python, a variable is a name bound to an object. The equals sign does not mean “these two things are mathematically equal forever.” It means: evaluate the expression on the right, then bind the name on the left to the resulting object.

Python name binding and rebinding A variable named item_count first points to the integer object 3. After reassignment, the name points to the integer object 4. The earlier object is no longer what that name refers to. 1 · Assignment item_count 3 2 · Reassignment after item_count + 1 item_count 4 3 · old binding
Read assignment as a change in what a name refers to. Reassignment does not edit the integer 3 into 4; it binds the name to a different integer object.

Start with the smallest useful assignment

Here is a complete example:

customer_name = "Maya"
item_count = 3

print(customer_name)
print(item_count)
Expected output
Maya
3

Python evaluates "Maya", then binds the name customer_name to that string object. It evaluates 3, then binds item_count to that integer object. Later, print(item_count) asks Python to look up the current object associated with the name.

Plain-English rule

Right side first. Name second. Think: “work out the value, then remember it under this name.”

Reassignment changes the binding

Programs change state. A cart can gain an item, a status can move from pending to shipped, and a running total can increase. Python handles this by letting a name be assigned again.

item_count = 3
item_count = item_count + 1
print(item_count)
Expected output
4

The second line is often confusing if you read it like school algebra. Python does not ask whether item_count equals itself plus one. It first reads the current value of item_count, calculates 3 + 1, gets 4, then rebinds item_count to the integer object 4.

A realistic example: order state

status = "pending"
print(status)

status = "paid"
print(status)
Expected output
pending
paid

The name stayed the same; the value it refers to changed. That is useful when a variable represents the current state of something.

Choose names that explain the job

Python will accept many legal names, but readable code needs more than legal syntax. Compare x = 3 with retry_count = 3. The second name tells another reader what the value means.

Hard to read
x = 3
y = 19.99
z = False

The values exist, but the code hides their purpose.

Clearer
item_count = 3
cart_total = 19.99
is_paid = False

The names reveal the domain without a separate comment.

Python variable names can contain letters, digits, and underscores, but they cannot begin with a digit. Names are case-sensitive, so total and Total are different names. For ordinary variables, Python code usually follows snake_case: lowercase words separated by underscores.

Beginner shortcut

Name the thing by what it means in the program: customer_name, retry_count, has_access. Avoid names that only describe the data type, such as string1 or number2.

Using a name before binding it raises NameError

If Python cannot find a binding for a name, it cannot guess what value you intended.

print(order_total)
Expected result
NameError: name 'order_total' is not defined

This error is often simpler than it first appears. Check whether the assignment runs before the use, whether you misspelled the name, and whether the value lives inside a function scope that is not available where you are trying to read it.

Common typo

Mistake
item_count = 3
print(items_count)

items_count is a different name, so Python raises NameError.

Corrected
item_count = 3
print(item_count)

Use one clear spelling consistently.

Python can bind several names in one statement

Python supports multiple assignment:

customer_name, item_count = "Maya", 3
print(customer_name)
print(item_count)
Expected output
Maya
3

The expressions on the right are evaluated before the target names are assigned. That is why this compact swap works:

left = "A"
right = "B"
left, right = right, left
print(left, right)
Expected output
B A

This is useful Python syntax, but do not compress assignments merely to look clever. Separate lines are often better when each value has a different meaning or deserves its own explanation.

The important trap: assignment does not copy mutable data

This is the point where the simple “variable is a box” analogy begins to break down. Two names can refer to the same mutable object. A list is mutable, which means its contents can change after creation.

items = ["book", "pen"]
backup = items

backup.append("notebook")

print(items)
print(backup)
Expected output
['book', 'pen', 'notebook']
['book', 'pen', 'notebook']

Why did items change? Because backup = items did not create another list. It created another name pointing to the existing list. The official Python tutorial describes this directly: simple assignment never copies data.

If you need a separate list, make a copy deliberately

items = ["book", "pen"]
backup = items.copy()

backup.append("notebook")

print(items)
print(backup)
Expected output
['book', 'pen']
['book', 'pen', 'notebook']
Good to know

list.copy() creates a shallow copy. If the list contains other mutable objects, those nested objects can still be shared. Deep-copy behavior belongs in a later chapter; the beginner lesson here is simply that assignment and copying are different operations.

A name can later refer to an object of another type

Python is dynamically typed. The object has a type; the variable name itself is not permanently declared as one type.

value = 10
print(type(value).__name__)

value = "ten"
print(type(value).__name__)
Expected output
int
str

Python allows this, but frequent unrelated type changes can make code harder to understand. A name should usually keep one clear meaning. If customer_id begins as an integer identifier and later becomes a full customer dictionary, the name is no longer communicating a stable contract.

Predict before you run it

What will this print?

count = 2
old_count = count
count = count + 1
print(count)
print(old_count)
Reveal the reasoning

It prints 3 and then 2. Integers are immutable objects. old_count = count binds both names to the integer 2. Reassigning count later points only that name at a different integer object; it does not mutate the integer 2 or rebind old_count.

Now compare that with the list aliasing example above. That contrast—rebinding a name versus mutating a shared object—is one of the most useful mental models you can build early in Python.

Guided state tracing

Trace the bindings step by step

When a variables problem feels confusing, slow the program down. Read one line, write down what each name refers to, then decide whether the line rebinds a name or mutates an existing object. These six traces turn that habit into a repeatable debugging skill.

Trace 1 of 6

An integer name is rebound, not edited in place

Trace what cart_total and saved_total refer to after each line.

Reveal the state changes
  1. cart_total = 12.50
    • cart_total → 12.5

    Python evaluates 12.50, then binds cart_total to that float object.

  2. saved_total = cart_total
    • cart_total → 12.5
    • saved_total → 12.5

    Both names now refer to the same immutable float object.

  3. cart_total = cart_total + 2.50
    • cart_total → 15.0
    • saved_total → 12.5

    The expression creates a new float result and only cart_total is rebound.

Takeaway: Rebinding one name does not rebind another name that happened to point at the same immutable object.

Trace 2 of 6

Two names can share one mutable list

Predict whether primary changes when backup appends an item.

Reveal the state changes
  1. primary = ["book", "pen"]
    • primary → list A

    A mutable list object is created and primary refers to it.

  2. backup = primary
    • primary → list A
    • backup → list A

    Assignment adds another name for the same list; it does not copy the list.

  3. backup.append("notebook")
    • primary → list A = ["book", "pen", "notebook"]
    • backup → list A = ["book", "pen", "notebook"]

    append mutates list A, so both names observe the changed object.

Takeaway: When aliases share a mutable object, mutation is visible through every name that still refers to that object.

Trace 3 of 6

List + and list += can create different aliasing outcomes

Compare rebinding from + with in-place mutation from +=.

Reveal the state changes
  1. items = ["book"]; alias = items
    • items → list A
    • alias → list A

    Both names start on the same list.

  2. items = items + ["pen"]
    • items → list B
    • alias → list A

    + builds a new list and items is rebound to it; alias still points to the original list.

  3. alias += ["ruler"]
    • items → list B = ["book", "pen"]
    • alias → list A = ["book", "ruler"]

    For a list, += normally mutates the existing list in place.

Takeaway: The operator matters because the state transition may be rebinding or mutation.

Trace 4 of 6

A shallow dictionary copy separates the outer mapping only

Track the outer dictionaries and the nested tags list.

Reveal the state changes
  1. order = {"status": "new", "tags": ["gift"]}
    • order → dict A
    • dict A.tags → list T

    The dictionary contains a reference to a nested mutable list.

  2. copied = order.copy()
    • order → dict A
    • copied → dict B
    • dict A.tags → list T
    • dict B.tags → list T

    dict.copy() creates a new outer dictionary, but nested mutable values are still shared.

  3. copied["tags"].append("priority")
    • order.tags → ["gift", "priority"]
    • copied.tags → ["gift", "priority"]

    Appending mutates the shared nested list, so both dictionaries appear to change there.

Takeaway: Shallow copy is enough for independent top-level immutable values, but not for nested mutable structures.

Trace 5 of 6

Unpacking binds several names from one value sequence

Trace how Python evaluates values first and then binds the targets.

Reveal the state changes
  1. first, *middle, last = [10, 20, 30, 40]
    • first → 10
    • middle → [20, 30]
    • last → 40

    The starred target collects the middle remainder into a new list.

  2. first, last = last, first
    • first → 40
    • middle → [20, 30]
    • last → 10

    Python evaluates the right-hand values before rebinding the two target names.

Takeaway: Multiple assignment is still ordinary binding; the compact syntax does not change the right-side-first rule.

Trace 6 of 6

None is a value; del removes a binding

Distinguish a name bound to None from a name that has no binding.

Reveal the state changes
  1. status = None
    • status → None

    None is a real singleton object. The name exists and is bound to it.

  2. del status
    • status → <unbound>

    del removes the current binding for the name.

  3. print(status)
    • NameError

    After the binding is removed, reading the name raises NameError.

Takeaway: Use None when “no value yet” is itself meaningful state; an unbound name is a different situation.

Debugging routine

Do not guess from the final output first. Trace the bindings, identify mutation versus rebinding, and then predict the output from the state you have written down.

Quick recap

  • A Python variable is a name bound to an object.
  • Assignment evaluates the right-hand side, then binds the target name.
  • Reassignment changes what a name refers to; it does not rewrite the old object.
  • Using an unbound name raises NameError.
  • Simple assignment does not copy mutable data; two names can refer to the same list.

Try it yourself

  1. Create product_name, unit_price, and quantity. Print a sentence that uses all three values.
  2. Start retry_count at 0, add one twice using reassignment, and predict the final value before running the code.
  3. Create a list called primary. Assign secondary = primary, append through secondary, and explain why primary changes. Then repeat with primary.copy().
  4. Create an intentional NameError, read the traceback, and fix the spelling or ordering mistake that caused it.
Official references

Check the language model against Python itself

This prepared chapter uses the current Python 3.14.7 documentation as its technical reference. The teaching language is intentionally simpler than the language reference, but the binding/copying model is not simplified into an incorrect “variables are boxes” rule.

Mini project · Put the model to work

Track a small shopping order without losing the state

You now know enough about names, rebinding, and mutable objects to build a tiny but realistic state model. The goal is not to write a complete shopping application. The goal is to practice choosing clear names, predicting each change, and noticing when assignment shares an object instead of copying it.

Project brief
  • Remember a customer name, item count, order status, and a list of item names.
  • Update the count and status through reassignment.
  • Create a separate snapshot of the item list before changing the live order.
  • Print both the current order and the snapshot so you can prove they are independent.

Step 1: bind the starting state

customer_name = "Maya"
item_count = 2
status = "pending"
items = ["book", "pen"]

Pause before adding more code. Four names now point at four objects. The string and integer values are immutable. The list is mutable, so another name can accidentally share the same changing object.

Step 2: make the snapshot deliberately

Common mistake
snapshot = items
items.append("notebook")

snapshot and items point to the same list. Appending through either name changes the one shared object.

Better for this project
snapshot = items.copy()
items.append("notebook")

The outer list is now separate. This is sufficient here because the list contains strings, not nested mutable objects.

Step 3: rebind the values that represent current state

item_count = item_count + 1
status = "paid"

These statements do not mutate the old integer or string. Python evaluates the right side, creates or reuses the resulting object, and binds the name to that result. The names now describe the current order state.

Step 4: print evidence, not guesses

print(customer_name)
print(item_count)
print(status)
print(items)
print(snapshot)
Expected output
Maya
3
paid
['book', 'pen', 'notebook']
['book', 'pen']

If your snapshot also contains "notebook", return to Step 2 and inspect whether you copied the list or only created another name for the same list.

Check your mental model
  1. Which lines rebind a name?
  2. Which line mutates an existing object?
  3. Why does changing status not change an earlier variable that happened to point to the old string?
  4. Why is items.copy() sufficient here, but potentially insufficient for a list containing nested dictionaries?
Extension

Add a discount_applied Boolean and a cart_total number. Predict the bindings before and after each reassignment. Then explain, in one sentence per line, whether the operation rebinds a name or mutates an object.