Skip to main content
Learning Center
Python Programming

Module 1: Values, Variables, and Expressions

Names and Expressions: Binding, Rebinding, and Order of Operations

What assignment actually does, why rebinding a name never changes values computed earlier, and how Python decides the order of an arithmetic expression.

Lesson 5 of 46 in the recommended order · About 25 min (estimate)

On this page

Outcome

By the end of this lesson you can read a run of assignments and say exactly what every name holds at every line, and you can predict what an arithmetic expression produces without running it.

Why it matters

"It should have updated" is one of the most common wrong beliefs in beginner code, and it comes from thinking of a name as a live link to a calculation. It is not. Assignment stores a result, once, and that result does not follow later changes to the things it was computed from.

Getting this right early removes a whole family of bugs that are invisible in a code review, because the code looks fine and the ordering is the problem.

Concept

name = expression does two things, in this order: it evaluates the expression on the right into a single value, then it binds the name on the left to that value. Nothing is remembered about how the value was produced.

Rebinding a name later replaces what the name points at. It does not reach backwards. If total was computed from base on line 3, changing base on line 5 leaves total exactly as it was.

The arithmetic operators are mostly what you would expect, with three worth memorising:

  • / always produces a float, even when it divides evenly: 10 / 5 is 5.0, not 5.
  • // is floor division: it divides and discards the fraction, keeping the type integral for two integers. 7 // 2 is 3.
  • % is the remainder: 7 % 2 is 1. It is how you ask "does this divide evenly".

Precedence follows ordinary maths: ** first, then *, /, //, %, then + and -. Parentheses override everything, and using them where a reader might hesitate is not a weakness. base + base * rate and (base + base) * rate are different programs, and only one of them is what you meant.

Augmented assignment, total += 500, is shorthand for total = total + 500. It reads the current value, computes, and rebinds, all on one line.

Read the code

base_value = 120000
option_years = 2
option_rate = 0.4

option_value = base_value * option_rate * option_years
ceiling = base_value + option_value

option_rate = 0.6

print(f"Option value: {option_value:,.0f}")
print(f"Ceiling: {ceiling:,.0f}")

Read it as a sequence of moments. After line 3, three names hold three numbers. Line 5 evaluates 120000 * 0.4 * 2 to 96000.0 and binds option_value to that number. Line 6 evaluates 120000 + 96000.0 to 216000.0 and binds ceiling.

Line 8 rebinds option_rate to 0.6. Both option_value and ceiling were computed two lines earlier and are untouched by it.

Three moments in the same run

1. After line 3

base_value
120000
option_years
2
option_rate
0.4
option_value
Not yet bound
ceiling
Not yet bound

Inputs are bound; the calculations have not run.

Next step in this same run

2. After line 6

base_value
120000
option_years
2
option_rate
0.4
option_value
96000.0
ceiling
216000.0

Lines 5 and 6 bind the calculated values.

Next step in this same run

3. After line 8

base_value
120000
option_years
2
option_rate
0.6
option_value
96000.0
ceiling
216000.0

Line 8 assigns option_rate: 0.4 → 0.6. The two calculated values stay the same.

Assignment computes a value once. Rebinding option_rate changes that name only; previously computed values remain bound to their earlier results.

Predict the output

Predict both printed lines exactly, including the thousands separators.

Check your prediction
Option value: 96,000
Ceiling: 216,000

If you predicted 144,000 and 264,000, you applied the new option_rate. That is the belief this lesson exists to correct: the rebinding on line 8 happens after both values were already computed and stored, so it changes nothing that was printed.

The :,.0f format spec asks for thousands separators and zero decimal places, which is why a float prints without a visible .0.

Modify the code

Move the line option_rate = 0.6 so it sits immediately after option_years = 2, before anything is computed. Predict both printed lines before you read on.

What changes, and why
Option value: 144,000
Ceiling: 264,000

Nothing about the arithmetic changed; only the moment the rate was bound. 120000 * 0.6 * 2 is 144000.0, and the ceiling follows. The whole difference between this run and the previous one is one line's position, which is exactly why "read the order, not just the lines" is a real reviewing skill.

Debug the bug

An assistant was asked for the total cost of a base year plus a ten percent contingency. It produced this and said it prints 132000.

base_value = 120000
contingency_rate = 0.1

total = base_value + base_value * contingency_rate
total = round(total)
total + 0

print(f"Total: {total}")
What's actually wrong

Two separate problems, and only one of them is what the assistant claimed.

The arithmetic is right: * binds tighter than +, so this is 120000 + 12000.0, which rounds to 132000. The printed number is correct.

The real defect is line 6, total + 0. It computes a value and throws it away, because nothing is assigned to. It is not an error, Python evaluates the expression and discards the result, so it runs silently and does nothing forever. Lines like this are a common residue of generated or half-edited code, and they matter: the next reader assumes a line that exists does something, and wastes time working out what.

If the intent had been to change total, the line would need to be total = total + 0 or total += 0. Delete it instead.

Try it yourself

A listing publishes a total contract value and a duration, both as text. Print the average value per month, rounded to two decimal places.

Loading this exercise…

Practical challenge (optional)

Optional: add an option-year calculation to your program. Introduce a rate and a number of option years, compute the ceiling value the way Read the Code did, and print the base, the option value, and the ceiling as three aligned lines. Then deliberately rebind the rate at the bottom of the file and confirm that nothing printed changes. Proving the rule to yourself once is worth more than reading it three times.

Sign in to track your progress on this exercise.

AI collaboration

Checkpoint

Given this sequence, what does each print show?

count = 4
count += 2
doubled = count * 2
count = 10
print(count)
print(doubled)
print(count % 3)
Answers

10, then 12, then 1.

count becomes 6 after the augmented assignment, so doubled is bound to 12. Rebinding count to 10 afterwards has no effect on doubled. 10 % 3 is 1, the remainder after dividing ten by three.

Sign in to track your progress on this exercise.

Summary and next step

Assignment evaluates then binds, rebinding never reaches backwards, / always yields a float, and parentheses are cheap insurance. Next: turning stored values into output a person can actually read, and the string operations that make published data consistent enough to compare.

learning.goultergroup.com

The interactive parts of this page have not loaded. Reading and links still work; reload the page to try again.