Python Part I · Module 3 · Operators and Expressions
and, or, not
The previous chapter gave you a way to produce a single yes-or-no answer. Real questions usually need more than one. A run is clean if it had no errors and it actually finished; it is suspect if it timed out or never produced an end marker. This chapter covers the three words that combine boolean values into one, which is a small amount of syntax carrying a lot of weight. It also covers something Python does that surprises people the first time they notice it — once the answer to a combined question is settled, Python stops working out the rest, which turns out to be a genuinely useful property rather than an optimisation detail. The chapter closes on a distinction that matters more to this audience than most: these three are not the bitwise operators.
Foundation11 min readPythonPart IFundamentalsBooleanLogic
Part I · Module 3 · Chapter 3.5 · and, or, not
1. Three Words
Python spells its logical operators as words rather than symbols:
passed = True
finished = False
print(passed and finished)
print(passed or finished)
print(not passed)False
True
FalseandproducesTrueonly when both sides areTrue.orproducesTruewhen at least one side isTrue.notproduces the opposite of what it is given.
Each is an expression, so each produces a value you can print or store — the model from Chapter 3.1, unchanged.
2. Reasoning Through Them
Take them one at a time rather than memorising a table.
and — both must hold
errors_clean = True
run_finished = True
print(errors_clean and run_finished)
run_finished = False
print(errors_clean and run_finished)True
FalseRead it as the English word. Was it clean and did it finish? If either half is false, the whole answer is false. Only both-true gives true.
or — one is enough
timed_out = False
crashed = True
print(timed_out or crashed)
crashed = False
print(timed_out or crashed)True
FalseDid it time out or did it crash? Either one is enough to make the answer true. Only both-false gives false.
Note this is the inclusive or — if both are true, the answer is still true. English sometimes means "one or the other but not both"; Python's or never does.
not — flip it
passed = True
print(not passed)
print(not not passed)False
Truenot takes one value rather than two, and gives the opposite.
3. Combining With Comparisons
The real use is joining comparison expressions from Chapter 3.4:
error_count = 0
coverage = 96.2
print(error_count == 0 and coverage >= 95.0)TrueTraced:
error_count == 0 and coverage >= 95.0
↓ ↓ each comparison is evaluated
True and True
↓
TrueBoth comparisons produced a bool first, and then and combined them. Comparisons bind more tightly than and, which is why no brackets were needed — Chapter 3.8 covers that formally, and §7 of this chapter shows when to add them anyway.
You can store the answer like any other value:
error_count = 0
coverage = 96.2
ready_to_sign_off = error_count == 0 and coverage >= 95.0
print(ready_to_sign_off)True4. Truth Tables
Now that each has been reasoned through, they compress. These are a summary, not the lesson.
and — true only when both are:
| A | B | A and B |
|---|---|---|
False | False | False |
False | True | False |
True | False | False |
True | True | True |
or — true when either is:
| A | B | A or B |
|---|---|---|
False | False | False |
False | True | True |
True | False | True |
True | True | True |
not — the opposite:
| A | not A |
|---|---|
False | True |
True | False |
5. Python Stops Early
Something worth knowing, because it explains behaviour you will otherwise find puzzling.
For A and B: if A is False, the answer is False whatever B is — so Python does not work out B at all.
For A or B: if A is True, the answer is True whatever B is — so again B is skipped.
This is called short-circuit evaluation, and it is not merely an optimisation. Here is a case where it is the difference between working and failing:
x = 0
print(x != 0 and 100 / x > 5)FalseThat ran without error. But 100 / x with x being zero is a ZeroDivisionError — Chapter 3.2 §6. It did not happen, because x != 0 was False, the answer was already settled, and Python never evaluated the division.
Reverse the order and the protection disappears:
x = 0
print(100 / x > 5 and x != 0)ZeroDivisionError: division by zeroSame two tests, same values, different order — and now the division runs before anything can stop it.
6. These Are Not the Bitwise Operators
An important distinction for this audience, and one Python makes easy to get wrong because both spellings exist.
print(True and False)
print(True & False)False
FalseSame answer — which is exactly why the confusion survives. Now with numbers:
print(2 and 3)
print(2 & 3)3
2Completely different. and and & are not two spellings of one operation.
and,or,notare logical operators. They work on whole values and answer a yes-or-no question.&,|,^are bitwise operators. They work on the individual bits of an integer, which is Chapter 3.7.
7. Parentheses for the Reader
Python has rules for the order these apply in, and Chapter 3.8 covers them. But even when the rules give the answer you want, brackets can make the intent obvious:
error_count = 0
timed_out = False
finished = True
print((error_count == 0) and (finished and not timed_out))TrueThose brackets change nothing about the result. They group the reading, so that a colleague scanning the line sees the two halves of the question immediately rather than reconstructing the order in their head.
That is a habit worth having. Chapter 3.8 makes the argument properly.
8. A Note on What and Gives Back
You may have noticed §6: 2 and 3 produced 3, not True.
and and or hand back one of the two values they were given, rather than always converting to True or False. When both sides are already booleans — which is every case in this chapter and almost every case you will write — the result is a boolean, so the distinction does not show.
The general rules for how non-boolean values behave in a logical context are Module 9 — Conditional Statements, where they matter. For now: keep both sides of and/or boolean and the behaviour is exactly the tables in §4.
9. Predict, Run, Observe
Predict each, then run.
passed = True
timed_out = False
coverage = 92.0
print(passed and not timed_out)
print(passed and coverage >= 95.0)
print(passed or coverage >= 95.0)
print(not (passed and timed_out))
print(coverage >= 90.0 and coverage < 95.0)
print(type(passed and timed_out))True
False
True
True
True
<class 'bool'>The fifth line is a range check, written the way Chapter 3.4 §6 wrote it as a chain. Both forms are valid; the chained 90.0 <= coverage < 95.0 reads slightly better.
10. Exercise
Exercise 1 — one question from two. Given error_count = 0 and finished = True, write one line that stores whether the run is clean — meaning no errors and it finished — and print it.
Exercise 2 — predict eight. With a = True and b = False, predict each: (a) a and b (b) a or b (c) not a (d) not b (e) a and not b (f) not (a or b) (g) a or not b (h) not a and not b
Exercise 3 — the order that matters. Predict what each does, then run:
x = 0
print(x != 0 and 100 / x > 5)
print(100 / x > 5 and x != 0)One works, one fails. Explain why in two sentences using the phrase "already settled".
Exercise 4 — the wrong operator. Predict both, then run:
print(6 and 3)
print(6 & 3)They differ. Say in one sentence what each operator was actually asked to do.
Exercise 5 — bracket it for a reader. This line is correct as written. Add brackets that change nothing but make the two halves obvious:
error_count == 0 and finished and not timed_out11. Summary
andneeds both sides true;orneeds at least one;notflips one value. All three are expressions producing a value.- They combine comparisons naturally —
error_count == 0 and coverage >= 95.0— because comparisons produce booleans and bind more tightly. - Truth tables summarise what reasoning already established; they are not the starting point.
- Python short-circuits. Once the answer is settled, the other side is never evaluated — which is why
x != 0 and 100 / x > 5is safe and the reverse order raises. - Put the test that makes the rest safe on the left.
and/or/notare not&/|/^. They agree on booleans and disagree on integers. Words for logic, symbols for bits.- Brackets can clarify without changing the result, and that is a good enough reason to add them.
- Keep both sides boolean and the behaviour is exactly the tables above; Module 9 covers the general case.
Two operator families left. The next one covers two questions that do not fit the pattern so far: whether something appears inside something else, and whether two names refer to the very same value.
Where this fits
Part of the Python curriculum.
