Logical Operators: and, or, not
Real decisions combine conditions: *"if you're 18 and have a ticket"* — *"if it's raining or cloudy"* — *"if not logged in."* Three operators handle all of it.
The truth table (the whole lesson on one screen)
print(True and True) # True
print(True and False) # False
print(False and False) # False
print(True or True) # True
print(True or False) # True
print(False or False) # False
print(not True) # False
print(not False) # TrueIn words:
The real usage: combining comparisons
age = 20
has_ticket = True
# Both conditions must hold
can_enter = age >= 18 and has_ticket
print(can_enter) # True
# Any one is enough
is_weekend = True
is_holiday = False
can_relax = is_weekend or is_holiday
print(can_relax) # True
# Flip a flag
is_raining = False
need_umbrella = not is_raining
print(need_umbrella) # True... wait, that's wrong on purpose!That last one is a bug I planted — if it's NOT raining, do you need an umbrella? Read not is_raining out loud: "not raining" → True → but you need an umbrella when it IS raining. The correct line: need_umbrella = is_raining. Read every `not` out loud. It catches these instantly.
Real conditions from real apps
# Login check
username_ok = username == "admin"
password_ok = password == "secret123"
if username_ok and password_ok:
print("Welcome!")
# Range check (the readable way)
if 0 < marks <= 100:
print("Valid marks")
# Discount eligibility
if is_student or is_senior:
price = price * 0.8
# Guard clause
if not is_logged_in:
print("Please log in first")That last pattern — if not something: exit early — is called a guard clause, and professional code uses it everywhere.
Truthiness — the sneaky superpower
Python can treat ANY value as True or False:
# These are all "falsy" (act like False):
print(bool(0)) # False
print(bool("")) # False — empty string
print(bool([])) # False — empty list
print(bool(None)) # False
# These are all "truthy" (act like True):
print(bool(42)) # True
print(bool("hi")) # True — non-empty string
print(bool([-1])) # True — non-empty listWhich enables beautifully short code:
name = input("Name: ")
if name: # "if name is non-empty"
print(f"Hello, {name}")
else:
print("You typed nothing!")if name: reads as "if there's a name" — Pythonic and clean.
Short-circuit evaluation
Python stops evaluating as soon as the answer is known:
print(False and crash_the_program) # False — crash_the_program never runs!
print(True or crash_the_program) # True — same, skippedand short-circuits on the first False; or short-circuits on the first True. Practical use — safe checks:
# If user is None, the second condition is never evaluated → no crash
if user is not None and user.age >= 18:
print("Adult user")Precedence: not > and > or
print(True or False and False) # True — "and" binds tighter: True or (False and False)
print((True or False) and False) # False — parentheses change itSame advice as arithmetic: when mixing, use parentheses to say what you mean.
Common Errors & Fixes
if x > 5 and x < 10: (or chain: 5 < x < 10).if not is_invalid: — consider renaming the flag is_valid and simplifying.✅ Checkpoint
True and not False? *(True — not False is True, then True and True)*age < 18 or has_id — True if the user is 16 with no ID? *(False — both sides False)*bool("")? *(False — empty string is falsy)*if user is not None and user.age >= 18 not crash when user is None? *(Short-circuit: the second check never runs)*Next: assignment operators — the shorthand that makes updates one character shorter.