Boolean Operators
Combine conditions with and, or, and not, the logical connectives of Python.
- Use and, or, and not to combine boolean expressions
- Understand short-circuit evaluation
- Apply De Morgan's laws in Python
- Write complex conditions clearly
Building conditions from conditions
Comparison operators hand you a single truth value: True or False. The door at the club asks two questions at once, “are you of age, and do you hold a ticket?”, and that conjunction is itself a condition. Python, like the logic you met in mathematics, offers the three connectives that combine propositions:
- is written
and - is written
or - is written
not
The three connectives
Their behavior is the truth table you already know. Write it out in Python and it reads identically:
True and True # True
True and False # False
False or True # True
not True # FalseWhere they earn their keep is gluing comparisons into one gate. A venue, a weather alert, a weekday check:
age = 20
has_ticket = True
if age >= 18 and has_ticket:
print("Welcome in")
temperature = 30
if temperature < 0 or temperature > 40:
print("Extreme weather!")
is_weekend = False
if not is_weekend:
print("Time to work")Each of these is a single question assembled from smaller ones, exactly the way from the last lesson assembled intervals.
Short-circuit evaluation
The full truth table lists four rows, but Python does not always need them. Evaluate and with : the answer is False regardless of , so is never computed. The same wall applies to or: once is True, the result is decided. Python reads left to right and stops at the first decisive answer.
That is not a performance nicety; it is a safety device:
x = 0
# No division ever happens — 0 is falsy, so the second half is skipped
result = x != 0 and 10 / x > 2Had Python evaluated both sides, would crash on division by zero. The word and is a pre-flight gate: it refuses to fly the second condition unless the first clears it. This is why Python writes and/or where C-family languages write &&/||, the words carry the same short-circuit behavior without the cryptic symbols.
De Morgan’s two swaps
Logic’s most reusable identities trade a negation across a connective:
- ,
not (A and B)≡not A or not B - ,
not (A or B)≡not A and not B
In Python, the negation of a joined condition becomes a joined condition of negations:
# These are equivalent:
not (age >= 18 and has_ticket)
age < 18 or not has_ticketThe rewritten form reads straight: the door opens to no one underage and to no one without a ticket. De Morgan’s laws are the tool for turning a dense not (…) you must untangle into the plain reading.
The truth tables, at a glance
| True | True | True | True |
| True | False | False | True |
| False | True | False | True |
| False | False | False | False |
And flips the single truth value: not True → False, not False → True.
A worked example: the club door twice told
One door, one verdict, two wordings. The admission rule refuses anyone who is not of age or holds no ticket:
age = 20
has_ticket = True
denied = not (age >= 18 and has_ticket) # False
denied_again = age < 18 or not has_ticket # False — De Morgan, equivalentThe first line says “it is not true that (of age AND with ticket)”; the second says “underage OR without ticket”, the two sides of De Morgan’s law, and both answer the same. The untangled version reads like the sentence it describes.
Common pitfalls
and/orreturn an operand, not a boolean.0 and 5is0;0 or 5is5. Python hands back the decisive value itself. Falsey 0 did the deciding, so 0 is returned.notbinds tighter than==.not a == bparses asnot (a == b), not(not a) == b. Parenthesize when unsure.- Words, not bitwise symbols.
True and FalseisFalse;True & Falseis a bitwise operation on booleans with different behavior. Reserve&/|for bit-level work. and/orare lazy in a way that hides bugs. If the decisive side is already truthy/falsy, the far side never runs,1 or missing_function()never calls the function. A dead half that never crashed can hide a name you forgot.
🧩 Challenges
🧩 Challenge, think first, then reveal
Without running it, predict: 0 and 5, 0 or 5, 3 and 5, 3 or 5. What pattern do you see?
💡 Answer: 0 and 5 → 0, 0 or 5 → 5, 3 and 5 → 5, 3 or 5 → 3. Pattern: and hands back the first falsey operand (or the last if all are truthy); or hands back the first truthy one (or the last if all are falsey).
🧩 Challenge, think first, then reveal
Rewrite not (x > 5 and y < 10) with De Morgan’s law. Is the rewrite easier to read?
💡 Answer: not (x > 5 and y < 10) ≡ x <= 5 or y >= 10, a straightforward reading with no compound negation to untangle.
🧩 Challenge, think first, then reveal
Write a condition for a leap year: divisible by 4, except centuries (divisible by 100) unless also divisible by 400. Use and, or, not.
💡 Answer: (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0), divisible by 4 but not by 100, or divisible by 400.
🤔 Socratic Questions
0 and 5yields0, notFalse. Why does Python return the deciding value rather than a boolean? When does that become useful?- If
orreturns the first truthy operand, what is"hello" or "world"? And"" or "world"? - Why does Python favor the words
and,or,notover the symbols&&,||,!? What does the plain English buy a reader?
✅ Quick check
1. What is True and False?
2. What does 0 or 5 evaluate to?
3. Which is equivalent to not (a and b)?