Skip to content
academia.sh

Lesson 04 / 18

Operators and Expressions

Arithmetic, comparison, and logical operators; precedence, associativity, and short-circuit evaluation.

Contents

The first lesson gave an example of a logic error: the expression measurement_1 + measurement_2 / 2 did not give the expected average. The error came from not knowing in which order the operators are applied.

This lesson defines those rules. The rules repeat across languages with small differences; what is common is that precedence agrees with the conventions of mathematics.

Arithmetic Operators

Operator Meaning Example Result
+ addition 12 + 18 30
- subtraction 18 - 12 6
* multiplication 6 * 7 42
/ real division 7 / 2 3.5
// integer division 7 // 2 3
% remainder 7 % 2 1
** exponentiation 2 ** 10 1024

The remainder operator is used more often than it appears: whether a number is even (n % 2 == 0), a counter cycling at fixed intervals, and distributing values across a fixed number of buckets are all written with this operator.

Comparison Operators

Comparison operators produce a boolean value: ==, !=, <, >, <=, >=.

Writing the equality test as == is meant to distinguish it from assignment, written =. Confusing the two produces a program that is valid but wrong in some languages; for this reason many languages forbid assignment inside a condition.

The distinction established in the previous lesson holds here too: == tests content equality, is (or its counterpart) tests whether it is the same object.

Some languages allow comparisons to be chained:

measurement = 12
print(0 < measurement < 100)          # True  — two comparisons and a logical 'and'

This notation is readable but not universal; in most languages the same expression gives a result different from what is expected, because 0 < measurement is computed first and its result is then compared with 100.

Logical Operators

Three logical operators combine conditions:

aa bb a and b a or b not a
false false false false true
false true false true true
true false false true false
true true true true false

This table is identical to the bit-level AND/OR tables from the previous course. The difference is that these operators work on the entire expression, not on individual bits.

Short-Circuit Evaluation

Logical operators have a distinguishing behavior: once the result is determined, the right side is never evaluated.

  • In the and operator, if the left side is false, the result is false; the right side is not examined.
  • In the or operator, if the left side is true, the result is true; the right side is not examined.

This is not merely a speed optimization, it is a programming tool: the condition required for the right side to run safely is secured by writing it on the left.

divisor = 0

# The division never happens because the left side is false; no error occurs.
if divisor != 0 and 10 / divisor > 1:
    print("large")
else:
    print("condition not met")      # condition not met

measurements = []

# The first element is not accessed because the left side is true.
if len(measurements) == 0 or measurements[0] > 10:
    print("empty or first measurement large")   # empty or first measurement large

If the order of the two conditions is swapped, the programs raise a runtime error. The order of conditions is therefore a matter of correctness, not style.

Precedence and Associativity

Precedence determines which of two different operators is applied first; associativity determines which direction operators of the same precedence group in.

The commonly used precedence order, from highest to lowest:

Order Operators
1 () grouping
2 ** exponentiation
3 unary - (sign)
4 *, /, //, %
5 +, -
6 comparisons
7 not
8 and
9 or
print(2 + 3 * 4)          # 14   — multiplication first
print((2 + 3) * 4)        # 20   — grouping changes precedence
print(2 ** 3 ** 2)        # 512  — exponentiation right to left: 2 ** (3 ** 2)
print(-2 ** 2)            # -4   — exponentiation before unary minus: -(2 ** 2)
print(10 - 3 - 2)         # 5    — subtraction left to right: (10 - 3) - 2
print(True or False and False)   # True — 'and' first

The logic error from the first lesson is explained by this table: in the expression measurement_1 + measurement_2 / 2, division is at order four and addition at order five; division is performed first.

Practical rule: knowing the precedence rules is necessary, but relying on the reader knowing them is not advisable. Using parentheses where confusion is possible does not substitute for knowing the rule — it makes the rule visible.

Compound Assignment

Updating a variable based on its own value is written so often that languages offer a short notation for it:

total = 0
total = total + 12       # explicit form
total += 18               # compound assignment: same operation
print(total)              # 30

counter = 10
counter -= 1                 # 9
counter *= 2                 # 18
counter //= 5                # 3
print(counter)                # 3

Compound assignment is not an operator but a notational shorthand: a += b means a = a + b. The shorthand has two benefits — the variable name is written once (lowering the chance of assigning to the wrong name), and the reader sees at a glance that the update is on the same variable.

This equivalence thins out for mutable types: in some languages, compound assignment updates the existing object in place instead of producing a new one. In a shared list this difference produces observable consequences; the distinction is an extension of the rule established around immutability.

One warning: compound assignment is also a statement, not an expression. It has no value; it cannot be placed inside another expression.

Same Operator, Different Meaning

An operator’s meaning depends on the types it is applied to:

print(3 + 4)              # 7        — numeric addition
print("3" + "4")          # 34       — string concatenation
print([1, 2] + [3])       # [1, 2, 3] — list concatenation
print("ab" * 3)           # ababab   — repetition

The same symbol performing a different operation across different types is called operator overloading. It provides convenience; in exchange, code that assumes the wrong type can silently produce an unexpected result. A + operation performed without knowing whether a value taken from the user is a number or a string is the typical example of this.

This connects directly to the next lesson’s subject: what happens when a value’s type differs from what is expected?

Summary

  • Among the arithmetic operators, integer division and remainder are used frequently in numeric problems.
  • Comparison operators produce a boolean value; == tests content equality and is not assignment.
  • and, or, and not combine conditions; their truth tables are identical to their bit-level counterparts.
  • In short-circuit evaluation the right side does not run once the result is determined; the order of conditions affects correctness.
  • Precedence determines which operator is applied first, associativity determines how operators of the same precedence group; exponentiation associates right to left.
  • The same operator can carry different meanings across different types.

Next Step

What happens when different types meet in an expression? Can a string be added to a number, how is a real number converted to an integer, and what is lost in these conversions? The next lesson takes up type conversion and the consequences of a lossy conversion.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close