Chapter 05 of 24 | Python
Expressions and operators
An expression combines values, names, and operators to produce a value. Python supports arithmetic, comparisons, Boolean logic, membership tests, identity tests, and operations specific to types such as string concatenation.
subtotal = 19.95 * 3
tax = subtotal * 0.08
total = subtotal + tax
print(f"Total: ${total:.2f}")
Arithmetic and precedence
Python's arithmetic operators include addition (+), subtraction (-), multiplication (*), division (/), floor division (//), exponentiation (**), and remainder (%). Division with / produces a float; floor division rounds toward negative infinity, so -7 // 3 is -3, not -2. Parentheses make intended grouping clear.
Precedence determines grouping, but long expressions are often clearer as named intermediate values. Avoid expressions whose correctness depends on remembering many precedence levels. Floating-point operations can introduce rounding, and integer division by zero raises ZeroDivisionError.
Comparisons and chained comparisons
score = 84
passed = score >= 60
in_range = 0 <= score <= 100
is_weekend = day == "Saturday" or day == "Sunday"
Unlike many languages, Python supports mathematical-style chained comparisons such as 0 <= x <= 100. The middle expression is evaluated once. Comparisons can also chain accidentally in a way that does not match your intention, so use parentheses or separate variables when logic is complicated.
Boolean short-circuiting and truthiness
and and or short-circuit: the second operand may not be evaluated. They return one of their operands, not necessarily a Boolean. Empty strings and collections, zero, and None are false in a condition; most other objects are true.
username = ""
display_name = username or "Guest"
if records and records[0]["active"]:
print("First record is active")
Use truthiness when “empty or absent” is the intended condition. Use an explicit comparison when zero, an empty collection, or None has a distinct meaning.
Augmented assignment and side effects
Operators such as += update a name or invoke an in-place operation. For immutable objects, the result is typically a new object; for mutable objects, an operation may modify the existing object. This distinction can affect aliases. Keep side effects out of complicated expressions so execution order remains easy to understand.
Beginner-to-master progression
- Beginner: use arithmetic, comparisons, and Boolean expressions.
- Practitioner: understand truthiness, short-circuiting, division, and numeric precision.
- Advanced: understand Python's data model methods such as
__add__,__eq__, and rich comparisons. Overloaded operators should preserve clear, unsurprising semantics.
Exercises
- Calculate the average of three values and avoid integer-like truncation assumptions.
- Write a range check using a chained comparison and test both boundaries.
- Create a small class that implements a meaningful comparison and explain the consistency rules it must follow.
Operator methods and reflected operations
When an operator is used with a user-defined object, Python may call special methods such as __add__ or __mul__. If the left operand cannot handle an operation, a reflected method such as __radd__ may be considered. Implement operator methods only when the operation has a natural meaning for the type.
Comparison methods should be coherent: equality should be symmetric in practice, and ordering should satisfy the assumptions made by sorting and ordered algorithms. Rich comparisons can return non-Boolean objects in specialized APIs, but ordinary domain types should usually return bool. Python does not automatically infer all comparison methods from one implementation unless a helper such as functools.total_ordering is deliberately applied.
Numeric protocols and precision traps
Python integers have arbitrary precision, so integer arithmetic does not wrap at a fixed machine width. This prevents many overflow errors but does not make huge integer calculations free: memory and processing time still grow with the number of digits. Floats are finite precision and follow the runtime's floating-point behavior. The decimal and fractions modules provide alternatives with different performance and exactness trade-offs.
from fractions import Fraction
exact = Fraction(1, 10) + Fraction(2, 10)
print(exact) # 3/10
Constructing a fraction from a float captures that float's exact binary value, which may be a large fraction rather than the intended decimal. Prefer numerator/denominator integers or decimal text when exact rational intent matters.
Mastery checkpoint
- Implement a two-dimensional vector with addition and equality, then test algebraic properties.
- Show why equality, ordering, and hashing must agree when a value type is used in sets and sorted collections.
- Compare float, Decimal, and Fraction for one domain and justify the representation, rounding policy, and performance cost.