Variables, objects & types
In many languages a variable is a box that holds a value. In Python it is better to think of a variable as
a name tag attached to an object. The object (the number, the text, the list) lives on its
own. The name just refers to it. x = 5 means "attach the tag x to the object 5".
This small shift explains why changing a list through one name changes it everywhere, while "changing" a
number never does.
Luggage tags, not boxes
Objects are suitcases in a hall. Names are luggage tags tied to them. You can tie two tags to the same suitcase, and anything packed into that suitcase is visible whichever tag you follow. Moving a tag to a different suitcase does nothing to the first suitcase. Python quietly throws away suitcases that have no tags left.
1. Assignment attaches a name
city = "Kochi"
population = 677_381 # underscores make big numbers readable; they are ignored
area_km2 = 94.88
is_coastal = True
mayor = None # None: "no value (yet)"
for value in (city, population, area_km2, is_coastal, mayor):
print(f"{value!r:>10} is a {type(value).__name__}")
Every object has a type that decides what you can do with it: you can add two ints and upper-case a string, but not upper-case an int. Python checks types while the program runs. That is dynamic typing: a name can refer to an int now and a string later, and the object always knows its own type.
2. Same object or equal objects?
id(x) gives an object's identity, a number unique while the object exists. a is b
asks "are these the same object?". a == b asks "do these have equal values?". Usually you want
==.
a = [1, 2, 3]
b = a # another tag on the SAME list
c = [1, 2, 3] # a different list that happens to be equal
print("a == b:", a == b, "| a is b:", a is b)
print("a == c:", a == c, "| a is c:", a is c)
b.append(4) # change the object through the name b...
print("a is now", a) # ...and a sees it: they are the same object
print("c is still", c)
Compare with None using is: if result is None:. For everything
else (numbers, strings, lists) use ==. x is 5 sometimes appears to work because
CPython reuses small numbers, then fails for larger ones. Python even warns you about it.
3. Mutable and immutable objects
Some objects can change in place (mutable): lists, dictionaries, sets. Others can never change
(immutable): numbers, strings, tuples, True/False, None. When you
"change" an immutable object, Python actually makes a new object and moves the name to it:
score = 10
before = id(score)
score += 5 # a NEW int object; the name moves
print("int: same object after += ?", id(score) == before)
word = "tea"
before = id(word)
word += "pot" # strings too: a new string
print("str: same object after += ?", id(word) == before, "→", word)
items = ["tea"]
before = id(items)
items += ["pot"] # a list changes IN PLACE
print("list: same object after += ?", id(items) == before, "→", items)
| Immutable (can't change) | Mutable (can change in place) |
|---|---|
int, float, bool, str, tuple, frozenset, None | list, dict, set, most objects you create from classes |
| safe to share between names: nobody can change them | sharing means changes are visible through every name (lesson 08 shows how to copy) |
4. Useful assignment forms
x, y = 3, 7 # assign several names at once
x, y = y, x # swap: no temporary variable needed
print("swapped:", x, y)
count = 0
count += 1 # count = count + 1
count *= 10 # count = count * 10
print("count:", count)
width = height = 100 # both names → the same object (fine for immutable values)
print(width, height)
TAX_RATE = 0.18 # UPPER_CASE = "please treat as a constant" (a convention only)
print(f"{TAX_RATE:.0%}")
5. Naming rules and conventions
Rules (enforced)
Letters, digits and underscores; cannot start with a digit; case-sensitive (total ≠ Total); cannot be a keyword such as class, for, if, None.
Conventions (PEP 8)
snake_case for variables and functions, PascalCase for classes, UPPER_CASE for constants. Descriptive names: unit_price, not up or x2.
A classic beginner accident is naming a variable after a built-in, which hides the built-in for the rest of the program:
list = ["tea", "rice"] # 'list' now means THIS list, not the built-in type
letters = list("abc") # so this fails
Pick another name (items, groceries) and the built-in keeps working. Editors warn you about this.
6. None: the "nothing here" value
discount = None # not decided yet
print(type(discount).__name__, discount is None)
discount = 0 # zero is a value, not "nothing"
print("zero is None?", discount is None)
def find_price(item): # a function that finds nothing returns None
return {"tea": 120}.get(item)
print(find_price("coffee"))
Recap
- Names refer to objects. Assignment attaches a name; it never copies an object.
- Every object has a type, checked while the program runs (dynamic typing).
==compares values,iscompares identity. Useisonly withNone.- Immutable objects never change (a new object is made); mutable ones change in place, visibly through every name.
Checkpoint
a = [1]; b = a; b.append(2). What is a?
n = 10; m = n; n += 1. What is m?
result has no value?
not result would also be true for 0, "" and [].