Skip to content

2.1.3 Operators and Expressions

Operators and conditional decision flowchart

Read the picture first: every decision starts from an expression. Comparisons and logical operators turn that expression into True or False, and the result decides which branch the program takes.

In this section, you’ll learn how to calculate and make decisions with data. Operators are not only used in math calculations; they also appear in model metric calculations, conditional filtering, loop decisions, and data cleaning logic. They are the first step in combining variables into program logic.

  • Master arithmetic operators, comparison operators, and logical operators
  • Understand operator precedence
  • Learn how to use assignment operators and membership operators
  • Be able to write correct conditional expressions

What you want to doCommon operators
Calculate values+-*/
Compare values>>===!=
Combine conditionsandornot
Check whether something is includedinnot in

You are developing an AI data processing script and need to:

  • Calculate model accuracy: correct / total * 100
  • Check whether it meets the target: accuracy >= 60
  • Check two conditions: accuracy >= 60 and loss < 0.5

All of these operations depend on operators. Operators are the symbols that tell Python “what to do with the data.”


The most basic mathematical operations:

OperatorMeaningExampleResult
+Addition5 + 38
-Subtraction5 - 32
*Multiplication5 * 315
/Division5 / 31.6667
//Floor division5 // 31
%Remainder5 % 32
**Exponentiation5 ** 3125
# Scenario: calculate some metrics for AI model training
total_samples = 1000 # Total number of samples
correct = 873 # Number of correct predictions
epochs = 50 # Number of training epochs
batch_size = 32 # Batch size
# Calculate accuracy
accuracy = correct / total_samples * 100
print(f"Accuracy: {accuracy}%") # 87.3%
# Calculate how many batches are needed to finish one epoch
batches_per_epoch = total_samples // batch_size
remaining = total_samples % batch_size
print(f"There are {batches_per_epoch} full batches in each epoch") # 31
print(f"The last batch has {remaining} samples") # 8
# Calculate an exponentially decayed learning rate
initial_lr = 0.01
decay = 0.95
current_lr = initial_lr * (decay ** epochs)
print(f"Learning rate at epoch {epochs}: {current_lr:.6f}") # 0.000769

This is a common point of confusion for beginners:

print(7 / 2) # 3.5 ← Normal division, result is float
print(7 // 2) # 3 ← Floor division, drops the decimal part
print(-7 // 2) # -4 ← Note! Rounds down, not toward zero
# A useful trick with remainder
print(10 % 3) # 1 ← 10 divided by 3 leaves remainder 1
print(15 % 5) # 0 ← Remainder is 0 when evenly divisible
# Check whether a number is odd or even
number = 42
if number % 2 == 0:
print(f"{number} is an even number") # 42 is even

The result of comparison operators is always a Boolean value (True or False):

OperatorMeaningExampleResult
==Equal to5 == 5True
!=Not equal to5 != 3True
>Greater than5 > 3True
<Less than5 < 3False
>=Greater than or equal to5 >= 5True
<=Less than or equal to5 <= 3False
# Scenario: judge model performance
accuracy = 87.3
loss = 0.35
print(accuracy > 90) # False —— accuracy did not exceed 90
print(accuracy >= 80) # True —— accuracy is at least 80
print(loss < 0.5) # True —— loss is below 0.5
print(accuracy == 87.3) # True —— accuracy is exactly 87.3

Python allows chained comparisons, which is not possible in many other languages:

latency_ms = 185
# Check whether latency is inside the acceptable API range
print(50 <= latency_ms <= 200) # True
# Equivalent to
print(50 <= latency_ms and latency_ms <= 200) # True, but the version above is more concise
# More examples
x = 5
print(1 < x < 10) # True
print(1 < x < 3) # False

Logical operators are used to combine multiple conditions:

OperatorMeaningExplanation
andAndOnly both true is true
orOrAt least one true is true
notNotNegates the value: true becomes false, false becomes true
tests_passed = True
has_review = True
has_rollback_plan = False
# and: both conditions must be satisfied
can_release = tests_passed and has_review
print(f"Can release: {can_release}") # True (tests passed and review is done)
# or: at least one condition is satisfied
has_safety_net = has_review or has_rollback_plan
print(f"Has safety net: {has_safety_net}") # True (review already provides one check)
# not: negate
needs_attention = not tests_passed
print(f"Needs attention: {needs_attention}") # False
accuracy = 92.5
loss = 0.15
training_time = 3.5 # hours
# Standard for a good model: accuracy > 90 and loss < 0.3
is_good_model = accuracy > 90 and loss < 0.3
print(f"Is it a good model: {is_good_model}") # True
# Need retraining: accuracy too low or loss too high
need_retrain = accuracy < 80 or loss > 1.0
print(f"Needs retraining: {need_retrain}") # False
# Practical model: good model and training time is reasonable
is_practical = is_good_model and not (training_time > 24)
print(f"Is it practical: {is_practical}") # True

Short-circuit safety check diagram

Python’s and and or have a smart feature called short-circuit evaluation:

# and: if the first condition is False, the second condition is not checked
# because the result is already guaranteed to be False
False and print("This line will not be executed")
# or: if the first condition is True, the second condition is not checked
# because the result is already guaranteed to be True
True or print("This line will not be executed either")

This feature is often used in real programming for safety checks:

# Check whether the list is empty before accessing an element (to avoid errors)
data = []
# If data is empty, len(data) > 0 is False, and the following part will not run
if len(data) > 0 and data[0] > 10:
print("The first element is greater than 10")

When reading short-circuit expressions, order matters. Put the cheapest and safest check first, and put the operation that may fail or cost more later. You will reuse this habit when checking files, lists, dictionaries, API responses, and model outputs.


In addition to the basic =, there are some shorthand forms:

OperatorEquivalent toExample
+=a = a + ba += 5
-=a = a - ba -= 3
*=a = a * ba *= 2
/=a = a / ba /= 4
//=a = a // ba //= 3
%=a = a % ba %= 2
**=a = a ** ba **= 3
completed_tasks = 0
completed_tasks += 2 # completed_tasks = 0 + 2 = 2
completed_tasks += 3 # completed_tasks = 2 + 3 = 5
completed_tasks -= 1 # completed_tasks = 5 - 1 = 4
completed_tasks *= 2 # completed_tasks = 4 * 2 = 8
print(f"Completed task points: {completed_tasks}") # 8

These shorthand forms are especially common in loops:

# Add up numbers from 1 to 100
total = 0
for i in range(1, 101):
total += i
print(f"The sum of 1 to 100 is: {total}") # 5050

in and not in are used to check whether a value is in a collection:

# Search in a string
print("Python" in "I love Python") # True
print("Java" in "I love Python") # False
print("Java" not in "I love Python") # True
# Search in a list
services = ["login-api", "search-api", "worker"]
print("login-api" in services) # True
print("billing-api" in services) # False
# Real-world application: check file extension
filename = "model.py"
if ".py" in filename:
print("This is a Python file")

is and is not are used to check whether two variables are the same object (not just equal in value, but the same thing in memory):

a = None
# Check whether it is None (recommended to use is, not ==)
print(a is None) # True
print(a is not None) # False
# The difference between is and ==
x = [1, 2, 3]
y = [1, 2, 3]
z = x
print(x == y) # True —— values are equal
print(x is y) # False —— not the same object (two different lists)
print(x is z) # True —— z points to x, so they are the same object

When an expression contains multiple operators, Python evaluates them according to precedence from high to low:

Priority (high → low)Operator
1 (highest)** exponentiation
2+x, -x positive/negative sign
3*, /, //, %
4+, -
5==, !=, >, &lt;, >=, &lt;=
6not
7and
8 (lowest)or
# Without parentheses
result = 2 + 3 * 4 # Multiply first, then add: 2 + 12 = 14
result = 2 ** 3 ** 2 # Exponentiation is right-to-left: 2 ** 9 = 512
# Parentheses make it clearer (recommended)
result = (2 + 3) * 4 # 20
result = (2 ** 3) ** 2 # 64

When a condition is hard to understand, do not stare at the whole expression at once. Split it into named intermediate variables:

accuracy = 87.3
loss = 0.35
latency_ms = 185
is_accurate_enough = accuracy >= 80
is_loss_ok = loss < 0.5
is_latency_ok = latency_ms < 250
can_demo = is_accurate_enough and is_loss_ok and is_latency_ok
print(is_accurate_enough, is_loss_ok, is_latency_ok, can_demo)

This gives you two benefits. First, the printed values show which small condition failed. Second, the variable names put business meaning into the code, which is easier to review than one long chain of symbols.

After you split the expression, you can print each intermediate variable as a minimal debugging trace. In data cleaning, model evaluation, and API monitoring, many bugs come from a reversed condition, a wrong threshold, or an and / or combination that does not match the intended rule.

Before trusting a condition, ask three quick questions:

  1. Did I compare values with ==, not assign with =?
  2. Did I make the threshold direction clear, such as latency_ms &lt; 250 instead of a vague variable name?
  3. Did I test at least one passing case and one failing case?

This small habit prevents many beginner bugs. Operators look simple, but most real program decisions are built from them.


In real projects, operators usually hide inside business rules. A model result may look good only when accuracy >= target, loss &lt; limit, and latency_ms &lt; budget are all true. A data row may be kept only when the label is not empty and the file extension is in the allowed list.

That is why this page is not only about symbols. Each operator should make a decision easier to read, test, and explain. When a condition matters to the product, give it a meaningful variable name and test both sides of the rule.

Before merging code that contains an important condition, read it out loud as a sentence. If the sentence sounds vague, split the expression into smaller variables. If the sentence sounds correct, create one example that should pass and one example that should fail.

This is also how you review beginner Python code. The syntax may be short, but the decision should still tell a clear story about the data and the intended rule.

If a teammate cannot predict the result from your variable names, the expression is doing too much work in one line.

The best operator code is not the shortest line. It is the line whose rule can be tested, reviewed, and changed without guessing.

Let’s combine the operators we learned today:

# API latency check
service = "Login API"
db_latency = 70 # ms
api_latency = 45 # ms
ui_latency = 80 # ms
# Calculate average latency
total_latency = db_latency + api_latency + ui_latency
average_latency = total_latency / 3
print(f"{service} average latency: {average_latency:.1f} ms") # 65.0
# Determine service status
is_fast = average_latency < 100
is_acceptable = 100 <= average_latency < 250
is_slow = 250 <= average_latency < 500
is_incident_risk = average_latency >= 500
print(f"Fast: {is_fast}") # True
print(f"Acceptable: {is_acceptable}") # False
print(f"Slow: {is_slow}") # False
print(f"Incident risk: {is_incident_risk}") # False
# Combined judgment
is_ready = is_fast and not is_incident_risk
print(f"Ready to demo: {is_ready}") # True

Use comparison operators and logical operators to determine latency status:

latency_ms = 185
is_fast = latency_ms < 100 # Fast
is_acceptable = latency_ms >= 100 and latency_ms < 250
is_slow = latency_ms >= 250 and latency_ms < 500
is_incident_risk = latency_ms >= 500
# Print results
print(f"Latency: {latency_ms} ms")
print(f"Fast: {is_fast}")
print(f"Acceptable: {is_acceptable}")
print(f"Slow: {is_slow}")
print(f"Incident risk: {is_incident_risk}")

Change the value of latency_ms and try different results.

Leap year rule: divisible by 4 but not by 100, or divisible by 400.

year = 2024
# Hint: use % to check divisibility, and combine conditions with and, or
is_leap = ___ # Complete this expression
print(f"Is {year} a leap year? {is_leap}")

Determine whether three sides can form a triangle (the sum of any two sides is greater than the third side):

a, b, c = 3, 4, 5
# Complete the condition
is_triangle = ___
print(f"Can sides {a}, {b}, {c} form a triangle? {is_triangle}")
Reference implementation and walkthrough
  1. With latency_ms = 185, only the “acceptable” branch should be true. Test 80, 320, and 650 to confirm the other branches.
  2. A leap-year expression can be year % 4 == 0 and year % 100 != 0 or year % 400 == 0. Parentheses can make it clearer.
  3. The triangle condition is a + b > c and a + c > b and b + c > a.
  4. Test non-examples and examples: 1, 2, 3 is false, 3, 4, 5 is true, and 2, 2, 3 is true.
  5. Parentheses are worth using in longer logical expressions even when operator precedence would technically work.

Keep this page’s proof of learning as a small evidence card:

Concept
variable, type, operator, input/output, branch, loop, structure, function, or module
Code
smallest runnable Python snippet for the concept
Output
printed value, type, branch result, loop trace, or returned value
Failure Check
type mismatch, indentation, off-by-one, mutable data, or import path issue
Expected Output
code plus printed result that proves the concept works
Operator typeCommon symbolsPurpose
Arithmetic+, -, *, /, //, %, **Mathematical calculation
Comparison==, !=, >, &lt;, >=, &lt;=Conditional checks, result is True/False
Logicaland, or, notCombine multiple conditions
Assignment=, +=, -=, *= etc.Assign values to variables
Membershipin, not inCheck whether an element is in a collection
Identityis, is notCheck whether two references point to the same object