Back to Blog
Python

Python Operator Precedence: Rules and Examples

python operator precedence: Understand how Python evaluates expressions with multiple operators, including precedence rules, associativity, and practical examples.

operator precedenceexpression evaluationpython syntaxparenthesesassociativity
Diagram illustrating Python operator precedence with parentheses controlling expression evaluation order.

Python operator precedence determines the order in which operators are evaluated in an expression. For example, in 2 + 3 * 4, multiplication has higher precedence than addition, so the result is 14, not 20. This rule is not arbitrary; it follows a standard mathematical convention that most languages share. Understanding this order is essential for writing correct code and for reading expressions written by others.

How Python Decides Which Operator Runs First

When an expression contains multiple operators, Python groups them according to a fixed precedence hierarchy. Operators with higher precedence are evaluated before those with lower precedence. For instance, * and / bind more tightly than + and -, so 10 - 4 / 2 evaluates as 10 - (4 / 2) and yields 8.0, not 3.0. The precedence rules are not configurable; they are part of the language grammar and are consistent across all Python implementations.

Consider the following example:

result = 5 + 3 * 2 ** 2 print(result) # 17, not 64

Here, exponentiation (**) has the highest precedence, so 2 ** 2 is evaluated first, giving 4. Then multiplication (3 * 4) runs, producing 12. Finally, addition (5 + 12) gives 17. If you expected 64, you would need parentheses: (5 + 3) * 2 ** 2.

The Operator Precedence Table

Python's precedence hierarchy is defined from highest to lowest. The following table lists the most common operators, grouped by precedence level. Operators on the same row have equal precedence and are evaluated according to associativity.

PrecedenceOperatorsDescription
1()Parentheses / grouping
2**Exponentiation
3+x, -x, ~xUnary plus, minus, bitwise NOT
4*, /, //, %Multiplication, division, floor division, modulo
5+, -Addition, subtraction
6<<, >>Bitwise shifts
7&Bitwise AND
8^Bitwise XOR
9``
10==, !=, <, <=, >, >=, is, is not, in, not inComparisons, identity, membership
11notBoolean NOT
12andBoolean AND
13orBoolean OR
14ifelseConditional expression (ternary)
15lambdaLambda expression
16:=Assignment expression (walrus)

This table is not exhaustive, but it covers the operators you will encounter most often. Notice that not has a lower precedence than comparisons, which often surprises developers. For example, not a == b is parsed as not (a == b), not as (not a) == b. The latter would require explicit parentheses.

Associativity: Left to Right and Right to Left

Precedence alone does not determine evaluation order when operators have the same precedence. Associativity fills that gap. Most binary operators in Python are left-associative, meaning they are evaluated from left to right. For example, 100 / 10 / 5 is evaluated as (100 / 10) / 5, giving 2.0, not 100 / (10 / 5) which would be 50.0.

The main exception is exponentiation, which is right-associative. So 2 ** 3 ** 2 is evaluated as 2 ** (3 ** 2), producing 512, not 64. This matches the common mathematical convention where exponentiation groups from the right.

Assignment operators and the walrus operator are also right-associative, as are the unary operators. In practice, you rarely rely on associativity for arithmetic because the result is usually unambiguous for commutative operations like addition and multiplication. However, for division, subtraction, and exponentiation, associativity changes the outcome.

Comparison Operators and Chaining

Python has a unique feature: comparison operators can be chained naturally. When you write a < b < c, Python evaluates it as a < b and b < c, but with an important subtlety: the middle expression b is evaluated only once. This chaining works for all comparison operators, including ==, !=, <, <=, >, >=, is, in, and their negations.

x = 5 if 0 < x < 10: print("x is between 0 and 10")

This is more readable than the explicit and version. However, chaining can lead to surprising behavior when combined with operator precedence. For instance, a == b == c is a chained comparison, not a comparison of (a == b) == c. The latter would compare the boolean result of a == b with c, which is rarely what you want.

Be careful when mixing comparisons with arithmetic. The expression 1 < 2 == True is parsed as 1 < 2 and 2 == True, which is True and False, yielding False. If you intended to compare the boolean result, you must use parentheses: (1 < 2) == True.

Common Mistakes with Precedence

A frequent error involves the not operator. Because not has lower precedence than comparisons, not a in b is parsed as not (a in b), which is usually correct. But not a == b is also not (a == b). If you want to negate the variable a before comparison, you need (not a) == b, which is almost never what you actually want.

Another common mistake is forgetting that bitwise operators have higher precedence than comparisons. For example, x & 1 == 0 is parsed as x & (1 == 0), which is x & False (since 1 == 0 is False, which is 0). This evaluates to 0 for any integer x, not the intended check for evenness. The correct expression is (x & 1) == 0.

Similarly, the ternary conditional expression has very low precedence. In a if b else c + d, the + binds tighter, so it is a if b else (c + d). If you need the condition to include the addition, you must write a if (b) else c + d or use parentheses around the entire conditional.

Using Parentheses to Control Evaluation

Parentheses override precedence and make the intended order explicit. They also improve readability, especially in complex expressions. Consider the difference between:

# Without parentheses: ambiguous to many readers result = a + b * c - d / e # With parentheses: clear and self-documenting result = (a + b) * (c - d) / e

Even when parentheses are not strictly necessary, they can prevent subtle bugs when someone later modifies the expression. For example, a and b or c is parsed as (a and b) or c, but if you intended a and (b or c), the result changes. Adding parentheses eliminates ambiguity.

In Python, parentheses also create tuples when used without commas, but that is a separate concern. In the context of expressions, they always group subexpressions.

Precedence in Boolean Expressions

Boolean operators have a well-defined precedence: not > and > or. This means that not a or b and c is parsed as (not a) or (b and c). This is a common source of confusion because many developers expect not to apply to the entire expression. To negate a conjunction, you must write not (a or b and c) or use De Morgan's laws.

Short-circuit evaluation also interacts with precedence. In a and b or c, if a is falsy, b is not evaluated, and the expression returns c if c is truthy. This behavior is intentional and often used for default values, but it can hide bugs if the precedence is misunderstood.

Consider this example:

value = None result = value is not None and value > 10 or "default"

Because and binds tighter than or, this is (value is not None and value > 10) or "default". If value is None, the first part is False, so result becomes "default". If value is 15, the first part is True, and result is True, not 15. To get the value itself, you would need a different approach.

Practical Implications for Readability and Maintenance

While Python's precedence rules are well-defined, relying on them exclusively can make code harder to read and maintain. A developer scanning if a & b == c must mentally parse the precedence to understand it. Adding parentheses makes the intent obvious without requiring the reader to consult a precedence table.

A good rule of thumb is to use parentheses whenever an expression mixes operators from different precedence levels, especially when the expression is not a simple arithmetic operation. This is not about avoiding the rules; it is about making the code self-documenting. For example:

# Clear and safe if (x & 1) == 0: print("even") # Also correct but less obvious if x & 1 == 0: print("even")

The first version is unambiguous and does not require the reader to know that & has higher precedence than ==. In a code review, the first version is less likely to be flagged as a potential bug.

Another maintainability concern is the use of chained comparisons. While they are a Python feature, they can be overused. a < b < c is fine, but a < b > c is confusing because it mixes directions. In such cases, explicit and or parentheses improve clarity.

Finally, when you write expressions that involve exponentiation and unary operators, remember that -2 ** 2 is parsed as -(2 ** 2), yielding -4, not 4. If you want the square of negative two, you must write (-2) ** 2. This is a common off-by-one error in numerical code.

By internalizing the precedence table and using parentheses judiciously, you can write expressions that are both correct and easy for others to understand. The goal is not to memorize every rule, but to recognize when an expression is likely to be misinterpreted and to add explicit grouping at those points.

python operator precedence: Practical Usage and Code Example | RYUSLOG DEV