Skip to main content
Advertisement

Ch 3.1 Arithmetic, Comparison, and Logical Operators

Operators are the fundamental tools for manipulating and comparing data. Python's operators are intuitive and powerful.

1. Arithmetic Operators

a = 17
b = 5

print(a + b) # 22 — addition
print(a - b) # 12 — subtraction
print(a * b) # 85 — multiplication
print(a / b) # 3.4 — division (always returns float)
print(a // b) # 3 — floor division (quotient, truncates decimal)
print(a % b) # 2 — remainder (modulo)
print(a ** b) # 1419857 — exponentiation (17^5)

# Difference between / and //
print(7 / 2) # 3.5 (always float)
print(7 // 2) # 3 (floor division)
print(-7 // 2) # -4 (negative numbers floor toward negative infinity!)

# ** operator
print(2 ** 10) # 1024
print(2 ** 0.5) # 1.4142... (square root)
print(2 ** -1) # 0.5

Math Functions

import math

# Using the math module
print(math.sqrt(16)) # 4.0 (square root)
print(math.floor(3.7)) # 3 (floor)
print(math.ceil(3.2)) # 4 (ceiling)
print(math.factorial(5)) # 120
print(math.log(100, 10)) # 2.0 (log base 10 of 100)
print(math.pi) # 3.141592653589793

# Built-in functions
print(abs(-42)) # 42 (absolute value)
print(round(3.14159, 2)) # 3.14
print(pow(2, 8)) # 256
quotient, remainder = divmod(17, 5) # (3, 2)

2. Comparison Operators

x = 10
y = 20

print(x == y) # False — equal
print(x != y) # True — not equal
print(x < y) # True — less than
print(x > y) # False — greater than
print(x <= y) # True — less than or equal
print(x >= y) # False — greater than or equal

# Comparison operators return bool
result = x < y
print(type(result)) # <class 'bool'>

Comparison Operator Chaining

A Python-exclusive feature — you can chain comparisons just like in mathematics.

score = 75

# Chaining is possible only in Python (other languages require and)
print(60 <= score < 80) # True (grade B)
print(0 <= score <= 100) # True (valid score)

# Equivalent expression (more verbose)
print(60 <= score and score < 80)

# Real-world use: grade classification
def get_grade(score: int) -> str:
"""Returns a letter grade for the given score."""
if not 0 <= score <= 100:
raise ValueError(f"Score must be between 0 and 100: {score}")
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
elif score >= 60:
return "D"
else:
return "F"

for s in [95, 85, 75, 65, 55]:
print(f"{s}: {get_grade(s)}")

3. Logical Operators

# and — True when both are True
print(True and True) # True
print(True and False) # False
print(False and True) # False

# or — True if at least one is True
print(True or False) # True
print(False or False) # False

# not — negation
print(not True) # False
print(not False) # True
print(not 0) # True
print(not "") # True

Short-Circuit Evaluation

Python does not evaluate the remaining expression once the result is determined.

# and: if the first operand is False, the second is not evaluated
def expensive_check():
print("Expensive operation running!")
return True

result = False and expensive_check() # "Expensive operation running!" is NOT printed
print(result) # False

# or: if the first operand is True, the second is not evaluated
result2 = True or expensive_check() # "Expensive operation running!" is NOT printed
print(result2) # True

# Safe access using short-circuit evaluation
user = {"name": "Alice", "profile": {"age": 30}}
# Safely access a dictionary key that may not exist
profile = user.get("profile")
age = profile.get("age") if profile is not None else None
print(age) # 30

Return Values of Logical Operators

Python's logical operators return the actual value, not just True/False.

# and — returns the first operand if falsy, otherwise returns the second
print(0 and "hello") # 0 (first is falsy)
print("hello" and "world") # 'world' (first is truthy)

# or — returns the first operand if truthy, otherwise returns the second
print(0 or "default") # 'default' (first is falsy)
print("value" or "default") # 'value' (first is truthy)

# Practical default value pattern
config_value = None
name = config_value or "default name"
print(name) # default name

# Watch out! 0, False, "" are also falsy
count = 0
result = count or 10 # Problem if 0 is a meaningful value!
print(result) # 10 (unintended result)

# Safer approach: check with is None
result = count if count is not None else 10
print(result) # 0 (correct result)

4. Assignment Operators

x = 10

x += 5 # x = x + 5 → 15
x -= 3 # x = x - 3 → 12
x *= 2 # x = x * 2 → 24
x /= 4 # x = x / 4 → 6.0 (float!)
x //= 2 # x = x // 2 → 3.0
x **= 2 # x = x ** 2 → 9.0
x %= 4 # x = x % 4 → 1.0

# Works with strings too
text = "Hello"
text += ", World!"
print(text) # Hello, World!

# Works with lists too
items = [1, 2, 3]
items += [4, 5] # same as extend
print(items) # [1, 2, 3, 4, 5]

5. is vs == — Identity vs Equality

# == — compares values (equality)
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True — same value

# is — compares object identity (same memory address)
print(a is b) # False — different objects

# is is True when both variables point to the same object
c = a # c points to the same object as a
print(a is c) # True

# Use id() to view memory addresses
print(id(a), id(b), id(c))
# e.g.: 140234567890 140234567920 140234567890
# a and c share the same address, b has a different address

Small Integer Caching Caveat

# Python caches integers in the range -5 to 256
a = 100
b = 100
print(a is b) # True (cached objects are shared)

# 257 and above are not cached
x = 1000
y = 1000
print(x is y) # False (may vary depending on interpreter implementation)
print(x == y) # True (values are equal)

# Conclusion: always use == for integer comparison, use is only for None/True/False
print(None is None) # True (recommended)
print(True is True) # True

6. Membership Operators — in, not in

fruits = ["apple", "banana", "cherry"]

print("apple" in fruits) # True
print("grape" in fruits) # False
print("grape" not in fruits) # True

# Works with strings too
text = "Hello, Python!"
print("Python" in text) # True
print("Java" not in text) # True

# In dictionaries, searches keys
config = {"host": "localhost", "port": 5432}
print("host" in config) # True (key search)
print("localhost" in config) # False (does NOT search values!)
print("localhost" in config.values()) # True (value search)

7. Practical Examples

Even/Odd Classification

def classify_number(n: int) -> str:
if n % 2 == 0:
return f"{n} is even."
else:
return f"{n} is odd."

for i in range(1, 8):
print(classify_number(i))

Grade Calculation (Production Version)

def calculate_grade(score: int) -> dict[str, str]:
"""Accepts a score and returns grade, pass/fail status, and a message."""
if not 0 <= score <= 100:
raise ValueError(f"Invalid score: {score}")

passed = score >= 60
grade = (
"A" if score >= 90
else "B" if score >= 80
else "C" if score >= 70
else "D" if score >= 60
else "F"
)
message = "Pass" if passed else "Fail"

return {"grade": grade, "passed": str(passed), "message": message}

print(calculate_grade(95)) # {'grade': 'A', 'passed': 'True', 'message': 'Pass'}
print(calculate_grade(45)) # {'grade': 'F', 'passed': 'False', 'message': 'Fail'}

Leap Year Check

def is_leap_year(year: int) -> bool:
"""Returns whether the year is a leap year.

Leap year conditions:
1. Divisible by 4, AND
2. Either not divisible by 100, OR
3. Divisible by 400
"""
return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

test_years = [1900, 2000, 2024, 2100, 2400]
for y in test_years:
print(f"{y}: {'Leap year' if is_leap_year(y) else 'Common year'}")

# Output:
# 1900: Common year (divisible by 100 but not 400)
# 2000: Leap year (divisible by 400)
# 2024: Leap year (divisible by 4, not divisible by 100)
# 2100: Common year (divisible by 100 but not 400)
# 2400: Leap year (divisible by 400)

Pro Tips: Safe Default Value Patterns Using and/or Short-Circuit Evaluation
# Pattern 1: Set default with or
# (Be careful when falsy values — 0, [], "" etc. — are meaningful)
def get_config(key: str, default: str) -> str:
env_value = None # Simulating a missing environment variable
return env_value or default

print(get_config("DB_HOST", "localhost")) # localhost

# Pattern 2: Safe chained access with and
user = {"profile": {"address": {"city": "Seoul"}}}
# Unsafe:
# city = user["profile"]["address"]["city"] # Risk of KeyError

# Safe chained access using and
profile = user.get("profile")
address = profile and profile.get("address")
city = address and address.get("city")
print(city) # Seoul

# Python 3.10+ — more concise with the walrus operator (detailed in Ch 3.4)
if (profile := user.get("profile")) and (addr := profile.get("address")):
city = addr.get("city", "Unknown")
print(f"City: {city}")

# Pattern 3: Conditional function call
import os
debug_mode = os.environ.get("DEBUG", "false").lower() == "true"
debug_mode and print("Debug mode enabled") # Runs only when debug_mode is True

You have learned all the arithmetic, comparison, and logical operators. The next chapter covers bitwise operators and special operators.

Advertisement