Lesson 02 / 18
Variables and Binding
How a name is bound to a value, the meaning of assignment, two models of variables, and naming conventions.
Contents
The previous lesson wrote measurement_1 = 12 and said that it stores a value. This
lesson fills in that statement: how is the bond between a name and a value
established, when does it change, and what does it correspond to in memory?
The question runs deeper than it looks, because languages model this bond in two different ways, and the difference between them produces observable consequences in later lessons.
Assignment Is a Binding Operation
The statement measurement_1 = 12 executes in two steps:
- The expression on the right of the equals sign is evaluated; a value is produced.
- That value is bound to the name on the left.
Order matters. The statement total = total + 5 looks like a meaningless equation in
mathematics; as a program it is clear: the expression on the right is computed first
(adding to the old value), and then the result is bound to the same name again.
For this reason the equals sign here means “bind,” not “equals.” Some languages use a
separate symbol such as := to avoid the confusion; the equality test, in turn, is
usually written ==.
What a Variable Holds: Two Models
Languages model a variable in two different ways. The difference becomes clear when considered together with the memory model established in the previous course.
The memory-cell model. A variable is a named region of memory reserved for a particular type. Assignment changes that region’s contents. The variable’s address is fixed for its entire lifetime; what changes is the bit pattern inside it. Most low-level languages use this model.
The name-object binding model. Values sit in memory as independent objects; a variable is a label attached to one of those objects. Assignment does not change the object’s content; it moves the label to a different object. Multiple labels can be attached to the same object.
The observable difference between the two models shows up in whether two variables share the same data:
a = [1, 2, 3] # a list object; 'a' is bound to it b = a # 'b' is bound to the same object; no copy b.append(4) print(a) # [1, 2, 3, 4] — also visible through 'a' print(a is b) # True — same object print(a == b) # True — same content c = [1, 2, 3, 4] print(a is c) # False — different objects print(a == c) # True — same content
The is operator asks about identity: are two names bound to the same object?
The == operator asks about equality: is their content the same? Confusing the
two questions is a common source of error in programs that work with shared data.
This distinction will come up again in the lesson on passing arguments to functions; the “by value or by reference” discussion there is a direct consequence of the two models here.
The way to break sharing is to produce an explicit copy. Copying itself has two depths: a shallow copy, which copies only the outer shell, and a deep copy, which copies the entire nested structure. In a shallow copy the inner objects are still shared; this is a common reason data assumed to be copied changes unexpectedly. Since the cost of copying grows with the size of the data, the choice between sharing and copying is a design decision.
Rebinding and Order
A name can be bound to different values over the course of a program. This brings with it an order-related trap: swapping the values of two variables cannot be done with two direct assignments.
x = 3 y = 7 # Wrong: x's old value is lost after the first assignment. x = y y = x print(x, y) # 7 7 — no swap happened # Correct: a temporary name is used. x, y = 3, 7 temp = x x = y y = temp print(x, y) # 7 3
Some languages support multiple assignment, and the swap collapses to a single line
(x, y = y, x); since the right side is fully evaluated before binding takes place
in this form, no temporary name is needed. This syntactic convenience does not change
the underlying rule.
An Undefined Name
What happens if a name is accessed before it is bound? The answer depends on the language’s variable model, and this is the second observable difference between the two models.
In the name-object binding model, the name is not yet bound to any object; accessing it gives an explicit error:
# print(total) -> runtime error: 'total' is not defined total = 0 # bound first print(total) # 0
In the memory-cell model, space has already been reserved for the variable, and a bit pattern already sits there. If the language does not zero out the reserved space, the value read is a meaningless leftover pattern from previous use. The program may or may not crash; it can give a different result on every run.
The danger of this second case is that the error has no symptom. For this reason, languages that use this model either require an initial value, produce a compiler warning, or automatically zero out the reserved space.
The general rule is the same under both models: a variable is bound to a meaningful value before it is used. Putting the initial assignment on the same line as the declaration makes this rule visible.
Naming
Names follow the language’s rules and the team’s conventions.
Rules belong to the language and are mandatory: which characters a name can
start with, whether case is distinguished, which words are reserved. Keywords such as
if or while cannot be used as variable names.
Conventions belong to the team and exist for readability:
- A name states what the variable holds:
total,measurement_count,largest. - Single-letter names are used only in narrow, conventional contexts (such as a loop counter).
- An abbreviation is spelled out if it cannot be assumed familiar to the reader.
- A single naming convention is used within a project; the same concept is not referred to by two different names.
Choosing names is not decoration. Code is read more often than it is written; names determine the effort every reader of the code spends. The software design curriculum details this topic under the heading of clean code.
Constants
Some names must stay bound to the same value for the entire run of the program: a conversion factor, an upper bound, a configuration value. These are called constants.
Languages handle this in two ways. Some languages enforce constancy: reassigning a constant is a compile error. Others rely on convention alone — the name is written in uppercase, and it is expected not to be changed.
MEASUREMENT_LIMIT = 100 # treated as constant by convention
The benefit of using a constant is that it prevents the same value from being
repeated in multiple places in the code. When the value changes, a single line is
updated; numbers scattered through the code do not need to be hunted down. Its
second, less obvious benefit is loading the name with meaning: the number 100
appearing in a condition tells the reader nothing, while the name
MEASUREMENT_LIMIT states what the bound represents.
Summary
- Assignment evaluates the expression on the right first, then binds the result to the name on the left; the equals sign means binding, not equality.
- Languages model a variable in two ways: a named memory cell, or a label attached to an object.
- Multiple names can be bound to the same object;
istests identity,==tests content equality. - The order of rebinding matters; a swap requires a temporary name or multiple assignment.
- Naming rules belong to the language, conventions belong to the team; a name should state what the variable holds.
- Constants gather repeated values into a single point.
Next Step
This lesson bound names to numbers and to lists, and the two were seen to behave differently. What exactly does a value’s type determine, though, and how many types are there? The next lesson takes up the basic data types and the operations each of them supports.
To keep your progress and take notes, Log in
My notes
Log in to take notes.