Introduction
In this lesson, you will go deeper into how Python decides which parts of an expression to evaluate first and how to safely combine multiple operators in a single line. You already know the basic operators; now you’ll learn how operator precedence, associativity, and chaining work together so that you can predict results, avoid subtle bugs, and write clearer code.
We will focus on:
- Understanding Python’s operator precedence in mixed expressions
- Using parentheses to control and clarify evaluation
- Mixing arithmetic, comparison, logical, and bitwise operators
- Building and evaluating chained comparisons (like
a < b <= c) - Tracing evaluation order in complex logical and comparison chains
Operator Precedence Refresher (With a Focus on Mixed Expressions)
Python evaluates expressions according to a fixed precedence order. Instead of memorizing the entire table, it’s more practical to:
-
Know the most common high-level groups:
- Parentheses:
(...) - Unary operators:
+x,-x,not x,~x - Arithmetic:
*,/,//,%before+,- - Bitwise:
<<,>>,&,^,| - Comparisons:
<,>,<=,>=,==,!=,is,in - Logical:
notbeforeandbeforeor
- Parentheses:
-
Use parentheses whenever an expression is not instantly obvious.
Example: Mixed Arithmetic and Logical Operators
x = 3
y = 10
z = 5
expr1 = x + y * z > 30 and z % 2 == 1
expr2 = (x + y) * (z > 3) or z == 0
print("expr1:", expr1)
print("expr2:", expr2)
# Output
expr1: True
expr2: True
Explanation:
y * zis evaluated beforex + ...because*has higher precedence than+.y * z = 10 * 5 = 50x + y * z = 3 + 50 = 5353 > 30→True
z % 2evaluated before==:5 % 2 = 1, so1 == 1→True.True and True→True, soexpr1isTrue.
For expr2:
z > 3→True, which behaves like1in arithmetic context.(x + y) * (z > 3)→(3 + 10) * 1 = 13 * 1 = 13(non-zero, so truthy →True).True or z == 0→True or False→True.
Notice how parentheses make the intended grouping explicit, even if precedence would give the same result.
Using Parentheses to Control and Communicate Intent
Precedence rules are predictable, but humans reading your code may not recall all of them. Parentheses serve two roles:
- Override default precedence (change the result).
- Clarify intent (even if they don’t change the result).
Example: A Bug from Missing Parentheses
age = 20
has_ticket = True
is_vip = False
allowed = age >= 18 and has_ticket or is_vip
print("allowed:", allowed)
fixed_allowed = (age >= 18 and has_ticket) or is_vip
print("fixed_allowed:", fixed_allowed)
# Output
allowed: True
fixed_allowed: True
In this scenario, both variables are True, but the precedence rule matters when values change.
Try changing values, for example:
age = 16
has_ticket = True
is_vip = True
allowed = age >= 18 and has_ticket or is_vip
fixed_allowed = (age >= 18 and has_ticket) or is_vip
print("allowed:", allowed)
print("fixed_allowed:", fixed_allowed)
# Output
allowed: True
fixed_allowed: True
Now change is_vip:
age = 16
has_ticket = True
is_vip = False
allowed = age >= 18 and has_ticket or is_vip
fixed_allowed = (age >= 18 and has_ticket) or is_vip
print("allowed:", allowed)
print("fixed_allowed:", fixed_allowed)
# Output
allowed: False
fixed_allowed: False
In more complex conditions, the difference between A and B or C and (A and B) or C can change behavior. Always group logically related conditions with parentheses instead of relying on and vs or precedence.
Comparing Operator Families in Mixed and Chained Expressions
When you mix comparison, logical, arithmetic, and bitwise operators, it’s crucial to know roughly where each stands:
- Arithmetic
+,-,*,/happens before comparisons. - Bitwise operators often sit between arithmetic and comparisons.
- Comparisons (
<,==,in,is) happen before logicaland/or.
Example: Arithmetic, Bitwise, Comparison, Logical Combined
a = 6 # 0b110
b = 3 # 0b011
c = 2
result = a & b + c > 3 and a ^ b == 5
print("result:", result)
# Output
result: True
Step-by-step evaluation:
b + c(arithmetic first):3 + 2 = 5.a & 5(bitwise AND):6 & 5→0b110 & 0b101 = 0b100 = 4.4 > 3→True.a ^ b(bitwise XOR):6 ^ 3→0b110 ^ 0b011 = 0b101 = 5.5 == 5→True.True and True→True.
Without understanding this ordering, it’s easy to mis-predict what’s being compared to what.
Chained Comparisons: a < b <= c
Python supports chained comparisons, which are evaluated in a mathematically natural way.
x = 5
y = 10
z = 20
expr_chained = x < y <= z
expr_separate = (x < y) and (y <= z)
print("expr_chained:", expr_chained)
print("expr_separate:", expr_separate)
# Output
expr_chained: True
expr_separate: True
How chaining works:
x < y <= zis evaluated as:(x < y) and (y <= z).- Important:
yis only evaluated once. - Python never interprets this as
(x < y) <= z.
You can chain more than two comparisons:
a = 1
b = 3
c = 5
d = 7
print(a < b < c < d) # True
print(a < b > c < d) # False
# Output
True
False
The second line is equivalent to (a < b) and (b > c) and (c < d). Here, b > c is 3 > 5, which is False, so the overall result is False.
Logical and Comparison Chains: Evaluation Order
When you mix logical operators (and, or, not) with comparisons, remember:
- Comparisons are evaluated before
and/or. notapplies to the expression immediately following it.andhas higher precedence thanor.
Example: Tracing a Mixed Logical & Comparison Chain
temp = 25
humidity = 60
raining = False
comfortable = temp > 20 and temp < 30 and (humidity < 70 or not raining)
print("comfortable:", comfortable)
# Output
comfortable: True
Evaluation trace:
temp > 20→25 > 20→True.temp < 30→25 < 30→True.- Inside parentheses:
humidity < 70→60 < 70→True. not raining→not False→True.True or Trueinside parentheses →True.- Final chain:
True and True and True→True.
If we remove parentheses, we risk misreading the expression, even if Python still handles precedence correctly. Always group logically related parts.
Common Pitfall Example
score = 85
passed = score >= 50
# Intention: (not passed) or score < 30
flag = not passed or score < 30
print("flag:", flag)
# Output
flag: False
Here, the intention is clear because of natural precedence, but if you wrote:
flag = not (passed or score < 30)
print("flag:", flag)
# Output
flag: False
This is a different logical condition. Parentheses communicate that you’re negating the whole union (passed or score < 30), not just passed.
Summary / Key Takeaways
In this lesson Operator Precedence and Chaining, you have learned:
- Operator precedence determines evaluation order in mixed expressions; arithmetic, bitwise, comparison, and logical operators each have their place in the hierarchy.
- Parentheses should be used liberally to override default precedence when needed and to make complex expressions immediately understandable.
- Different operator families interact in a defined way, with arithmetic evaluated before bitwise, comparisons before logical
and/or, andnotapplied to the nearest subexpression. - Chained comparisons like
a < b <= care evaluated as logical conjunctions, equivalent to(a < b) and (b <= c), with shared operands only evaluated once. - Logical operators and comparisons combine predictably, where comparisons are resolved first and then combined via
and,or, andnotaccording to their precedence. - Parentheses are essential to avoid subtle logical bugs, especially in expressions mixing
andandor, or when negation should apply to a group of conditions. - Tracing expressions step-by-step—from inner operations to outer logical combinations—is a practical technique for debugging and verifying complex conditions.
- Readable conditions are preferable to clever one-liners, and explicit grouping often matters more than relying solely on knowledge of precedence rules.
