- Explain Python's name-binding model and how it differs from the 'variable as box' metaphor
- Distinguish between the core data types: int, float, bool, str, and None
- Apply arithmetic, comparison, and logical operators to Python values
- Convert between types using int(), float(), str(), and bool()
- Differentiate between identity (is) and equality (==) and explain when each is appropriate
Every useful program needs to remember things. A calculator needs to remember the number you just typed. A web server needs to remember who is logged in. A game needs to remember the score. In most programming languages, the mechanism for remembering things is called a variable — but Python's variables work differently from what you might expect if you are coming from C or Java, and understanding the difference early will save you from a whole category of subtle bugs.
Names, Not Boxes
The most common metaphor for a variable is a box: you put a value inside it, and the box holds that value. This metaphor works reasonably well in languages like C, where a variable really is a named region of memory that stores a value directly. In Python, it is misleading.
A Python variable is a name that refers to an object. Think of it not as a box containing a value, but as a sticky label attached to an object. The object exists somewhere in memory; the name is just a way to find it. You can attach multiple labels to the same object, and you can peel a label off one object and stick it on another.
x = 42 # The name 'x' now refers to the integer object 42
y = x # The name 'y' now refers to the same object
x = "hello" # 'x' now refers to a string — the integer 42 is unchanged
print(y) # Output: 42 — 'y' still refers to the original object
This is called name binding. The = sign does not mean "store this value in this box". It means "make this name refer to this object". The distinction becomes crucial when you work with mutable objects like lists, but even with simple values, it shapes how Python thinks about everything.
Dynamic Typing
In C or Java, you must declare the type of a variable before you use it: int x = 42;. In Python, you do not. A name can refer to an integer one moment and a string the next:
value = 10 # value refers to an int
value = "ten" # now it refers to a str — perfectly legal
value = 3.14 # now a float
This is called dynamic typing. The type belongs to the object, not to the name. Python checks types at runtime — when the code actually executes — rather than at compile time. This gives you flexibility and speed of development, but it also means type errors only appear when the offending line runs, not when you save the file.
You can always check what type an object has:
print(type(42)) # Output: <class 'int'>
print(type("hello")) # Output: <class 'str'>
print(type(3.14)) # Output: <class 'float'>
The type() function returns the class of the object. For type checking in real code, prefer isinstance(), which also handles inheritance:
isinstance(42, int) # True
isinstance(42, (int, float)) # True — checks against multiple types
Integers
An int in Python represents a whole number — positive, negative, or zero. Unlike C or Java, Python integers have arbitrary precision: they can be as large as your memory allows. There is no overflow, no wrapping, no silent truncation.
small = 42
big = 2 ** 100 # 2 to the power of 100
print(big) # Output: 1267650600228229401496703205376
That is a 31-digit number, and Python handles it without breaking a sweat. This is one of those features that seems like a curiosity until the day it saves you from a bug that would take hours to find in C.
You can write integers in several bases:
decimal = 255 # Base 10 (default)
binary = 0b11111111 # Base 2
octal = 0o377 # Base 8
hexadecimal = 0xFF # Base 16 — all four are the same value
For large numbers, underscores improve readability:
population = 8_100_000_000 # Much clearer than 8100000000
Floats
A float represents a decimal number. Internally, Python uses IEEE 754 double-precision floating point, which gives you about 15-17 significant digits of precision.
pi = 3.14159265358979
temperature = -40.0
avogadro = 6.022e23 # Scientific notation: 6.022 × 10²³
Floats have a well-known trap. Try this:
print(0.1 + 0.2) # Output: 0.30000000000000004
This is not a Python bug — it is a consequence of how binary floating-point arithmetic works. The number 0.1 cannot be represented exactly in binary, just as 1/3 cannot be represented exactly in decimal. For most purposes the error is negligible, but for financial calculations or exact arithmetic, use the decimal module from the standard library.
Booleans
A bool has exactly two possible values: True and False. Note the capital letters — true and false are not valid Python.
is_active = True
has_access = False
print(type(True)) # Output: <class 'bool'>
Booleans are a subclass of integers in Python: True is 1 and False is 0. This means you can do arithmetic with them, though whether you should is a matter of style:
print(True + True) # Output: 2
print(False * 100) # Output: 0
Comparisons produce booleans:
print(5 > 3) # Output: True
print(10 == 20) # Output: False
print("a" < "b") # Output: True — alphabetical comparison
Strings
A str is a sequence of Unicode characters. You can create strings with single quotes, double quotes, or triple quotes for multi-line text:
name = 'Ada'
greeting = "Hello, world!"
paragraph = """This is a long string
that spans multiple lines.
Python preserves the line breaks."""
Single and double quotes are interchangeable — use whichever avoids the need for escaping. If your string contains an apostrophe, use double quotes; if it contains a quotation mark, use single quotes.
Strings support a rich set of operations:
# Concatenation and repetition
full = "Hello" + " " + "world" # "Hello world"
line = "-" * 40 # forty dashes
# Length, indexing, and slicing
print(len("Python")) # Output: 6
print("Python"[0]) # Output: 'P' (zero-indexed)
print("Python"[2:5]) # Output: 'tho'
# Useful methods
print("hello".upper()) # Output: 'HELLO'
print(" spaces ".strip()) # Output: 'spaces'
print("a,b,c".split(",")) # Output: ['a', 'b', 'c']
f-strings (formatted string literals) are the modern way to embed expressions inside strings:
name = "Guido"
age = 68
print(f"{name} is {age} years old.") # Output: Guido is 68 years old.
The f prefix tells Python to evaluate anything inside curly braces as an expression. F-strings are fast, readable, and the method you should reach for first.
None
None is Python's null value — it represents the deliberate absence of a value. It is not zero, not an empty string, not False. It is its own thing.
result = None
print(result) # Output: None
print(type(result)) # Output: <class 'NoneType'>
Functions that do not explicitly return a value return None by default. You will see it often, and the convention for checking it is to use is rather than ==:
if result is None:
print("No result yet.")
Arithmetic Operators
Python supports the standard arithmetic operations, plus a few that other languages lack:
print(10 + 3) # Output: 13 — addition
print(10 - 3) # Output: 7 — subtraction
print(10 * 3) # Output: 30 — multiplication
print(10 / 3) # Output: 3.3333333333333335 — true division (always float)
print(10 // 3) # Output: 3 — floor division (rounds down)
print(10 % 3) # Output: 1 — modulo (remainder)
print(10 ** 3) # Output: 1000 — exponentiation
The distinction between / and // trips up many beginners. True division (/) always returns a float, even if the result is a whole number: 10 / 2 gives 5.0. Floor division (//) discards the fractional part and returns an integer (if both operands are integers).
Comparison and Logical Operators
Comparison operators produce booleans:
print(5 == 5) # True — equal
print(5 != 3) # True — not equal
print(5 > 3) # True — greater than
print(5 < 3) # False — less than
print(5 >= 5) # True — greater than or equal
print(5 <= 3) # False — less than or equal
Python also supports chained comparisons, which is unusual and wonderfully readable:
x = 5
print(1 < x < 10) # True — 'x is between 1 and 10'
print(1 < x < 3) # False
Logical operators combine booleans:
print(True and False) # False — both must be True
print(True or False) # True — at least one must be True
print(not True) # False — inverts the value
Note that Python uses English words — and, or, not — rather than the symbols &&, ||, ! that C and Java use.
Type Conversion
You can convert between types explicitly:
int("42") # 42 — string to integer
float("3.14") # 3.14 — string to float
str(42) # "42" — integer to string
bool(0) # False — zero is falsy
bool(42) # True — any non-zero number is truthy
int(3.9) # 3 — truncates, does not round
Invalid conversions raise errors:
int("hello") # ValueError: invalid literal for int()
This is Python being explicit rather than guessing what you meant. If a conversion does not make sense, Python tells you rather than silently producing garbage.
Identity and Equality
Python has two ways to compare objects, and the difference matters.
== tests equality — do these two objects have the same value?
is tests identity — are these two names referring to the exact same object in memory?
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True — same value
print(a is b) # False — different objects
print(a is c) # True — same object (c is another label for a)
You can inspect an object's identity with id(), which returns its memory address:
print(id(a)) # e.g., 4385427200
print(id(c)) # Same number — same object
print(id(b)) # Different number — different object
The rule of thumb: use == for value comparison, which is what you want 99% of the time. Use is only for checking None (if x is None) and in rare cases where you deliberately need to know whether two names point to the same object.
Every value in Python is an object, every variable is a name, and every name is just a reference. This model is simple, consistent, and — once you internalise it — surprisingly powerful. The types you have met in this chapter are the building blocks of every Python program. In the next chapter, you will learn how to make decisions with them.