Topic 1.8
Comparisons and Logic
In one line
Comparison operators (==, !=, <, >, <=, >=) ask questions and answer with True or False. and, or and not combine answers. == asks "same value?", is asks "same object?", and and/or stop early and return one of their operands.
Think of it like this
The rules at a cinema. "You may enter if you are 12 or older and have a ticket." The two small questions (age? ticket?) each have a yes/no answer, and the word "and" combines them. Python's comparisons ask the small questions; and, or and not combine the answers.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Comparison
- An expression such as
a < bthat gives True or False. - Logical operator
and,or,not, which combine true/false answers.- Chained comparison
- Several comparisons in a row, like
1 < x < 10. - Equality (`==`)
- True when two values are equal.
- Identity (`is`)
- True when two names point to the very same object.
- Short-circuit
- Stopping as soon as the result is known, without running the rest.
- Operand
- One of the values an operator works on.
- De Morgan's laws
- Rules for turning
not (a and b)into(not a) or (not b)and the other way round.
Step by step
01Asking questions
A comparison produces a bool you can print, store, or later use to choose what to do (Phase 2). Notice 3 == 3.0 is True: the comparison is about value, and 3 and 3.0 are the same number. A text "3" is a different kind of thing, so "3" == 3 is False, and Python does not complain.
age = 25
print(age > 18, age == 25, age != 25, age >= 30) # True True False False
print("3" == 3, 3 == 3.0) # False True02Chains
18 <= age < 30 asks whether age sits between 18 (included) and 30 (not included). Python evaluates age once and checks both sides, and stops if the first check is False. Most other languages need age >= 18 && age < 30.
print(18 <= age < 30, 18 <= age < 20) # True False
print(1 < 2 < 3, 1 < 3 < 2) # True False03== versus is
Two lists with the same contents are *equal*, but they are two separate objects in memory, so is says False. same = first makes a second name for the same list, so first is same is True. (A list is a container of values written in square brackets; Phase 4 covers them.)
For None, there is only ever one None, so x is None is the exact, fast question.
first = [1, 2]
second = [1, 2]
same = first
print(first == second, first is second, first is same) # True False True04and, or, not and their order
not runs first, then and, then or. Use parentheses when you mix them, because few people remember the order. 1 or 2 and 0 is 1 or (2 and 0): 2 and 0 is 0, and 1 or 0 is 1.
print(True and False, True or False, not True) # False True False
print(1 or 2 and 0) # 105Short-circuit and returning operands
In True or 1 / 0 Python reads the left side, sees that it is true, and never looks at 1 / 0, so no error. In False and 1 / 0 the left side is already false, so again the right side is skipped.
Because of this, or is used to pick a default: name or "stranger" is the name if it is not empty, otherwise "stranger". Be careful when 0 or an empty list is a legitimate value; use if x is None instead (Topic 1.4).
print("" or "default") # default
print("Asha" or "default") # Asha
print(0 and 5, 3 and 5) # 0 5
print(True or 1 / 0) # True (no ZeroDivisionError)06De Morgan's laws
"Not (adult and has ticket)" means "not an adult, or no ticket". The rule: pushing not inside flips and to or and or to and. This lets you rewrite a confusing condition into an easier one; the two forms always give the same answer, as the examples show.
a = True
b = False
print((not (a and b)) == ((not a) or (not b))) # True
print((not (a or b)) == ((not a) and (not b))) # TrueTry it yourself
- 1
Between two numbers
Set
temperature = 22and print20 <= temperature <= 25. Change it to 30 and 10 and check each time. - 2
Predict the operand
Before you run, predict
0 or 7,5 or 7,0 and 7,5 and 7and"" or "x". Check yourself. - 3
Prove De Morgan
Set
aandbto each of the four combinations of True and False, and check the first law holds every time.
Code & diagrams
Note first == second is True but first is second is False.
Expected output
True True False False
True False
True False
False True False
True True True True False
True False True
True FalseThe two True or / False and lines would crash if the right side ran.
Expected output
default
Asha
0 5
last
stranger
True
False
True
True
True False True
1or replaces every falsy value, so a retries setting of 0 and an empty greeting are lost. Testing is None keeps them. The last lines show nan inside a list and a chain. The def lines are functions (Phase 5): read each as a named recipe that gives back one value. The dictionary is Phase 4.
Expected output
3 0
'Hello' ''
30 30
False True False
True True True TrueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use is with a number
Compare a variable to a number with is.
x = 5
print(x is 5)Break #2
Put = where you meant ==
Write a comparison with one equals sign inside print.
age = 12
print(age = 12)Myth vs fact
Myth
a == b and a is b mean the same thing.
Fact
== compares values; is compares identity (the very same object). Equal values can still be separate objects.
Myth
and and or always return True or False.
Fact
They return one of their operands. "" or "default" is "default", and 3 and 5 is 5.
Myth
0 < x < 10 is a mistake because it compares a bool.
Fact
Python chains comparisons. It means 0 < x and x < 10, with x evaluated once.
Myth
"Z" < "a" is False because Z comes later in the alphabet.
Fact
Strings compare by character code, and all capitals come before small letters. "Z" < "a" is True.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
x is Nonecompiles to a single pointer comparison, which is the fastest check there is.x == Nonecallsx.__eq__, which a class may override. - ▸
Short-circuiting is used for safe guards:
obj is not None and obj.value > 3never reads.valueonNone. The same guard pattern shows up inif user and user.is_admin:throughout web code. - ▸
NumPy arrays and pandas columns do not support
and/oron whole arrays. They use&,|and~with parentheses, becauseandneeds a single True or False (Phase 14). - ▸
Floating-point comparisons need
math.isclose(Topic 1.3). Every ordering and equality comparison withnanis False, and onlynan != nanis True. A subtle exception: containers check identity before equality, sonan in [nan]isTrueand[nan] == [nan]isTruewhen the very samenanobject is in both, butfloat("nan") in [float("nan")]isFalse. - ▸
ison numbers and strings is implementation behaviour. CPython keeps one object for each int from -5 to 256, and the compiler may merge equal constants in one code block, soa = 1000; b = 1000; a is bcan beTruein one file andFalseat the prompt. Interviewers ask this to see if you know the answer is "undefined, use==". Since Python 3.8 the compiler warns aboutiswith a literal (SyntaxWarning). - ▸
A chained comparison evaluates each middle operand once and short-circuits:
a < f() < bcallsf()one time, and ifa < f()is False the last comparison never runs. It is not limited to numbers or to one kind of operator:x == y == z,a < b > cand0 < x is not Noneare all legal, which is also whya == b is Truedoes not mean what beginners expect.and/orreturn operands, butnotalways returns a real bool.
Remember this
- 1
The comparison operators are
==(equal),!=(not equal),<,>,<=and>=. Each gives abool. Do not confuse==(a question) with=(an assignment):age = 12stores,age == 12asks. Numbers compare by value, so3 == 3.0isTrue; text and numbers never match:"3" == 3isFalse. - 2
Python allows chained comparisons:
18 <= age < 30means18 <= age and age < 30, and it reads like maths.1 < 3 < 2isFalse. Strings compare alphabetically by character code:"apple" < "banana"isTrue, but"Z" < "a"is alsoTrue, because all capital letters have lower codes than small letters. - 3
==asks whether two values are equal;isasks whether two names point to the same object (an object is one value stored in memory). Two separate lists[1, 2]and[1, 2]are==but notisthe same. Use==for values, andisonly for the singletonsNone,TrueandFalse(values of which only one copy exists), as inx is None.x is not Noneis the readable negation. - 4
and,or,notcombine booleans.A and Bis true only if both are.A or Bis true if at least one is.not Aflips. In a mixed expressionnotruns first, thenand, thenor, so1 or 2 and 0is1 or (2 and 0). - 5
andandorshort-circuit: they stop as soon as the answer is known.True or 1 / 0does not divide, becauseoralready knows the answer is true. They also return one of the original values, not just True or False:"" or "default"is"default", and3 and 5is5. The rule:orreturns the first truthy operand (or the last),andreturns the first falsy operand (or the last). - 6
That behaviour gives a handy idiom for defaults:
name or "stranger". It also contains a trap: it replaces every falsy value, including a valid 0. De Morgan's laws help simplify conditions:not (a and b)equals(not a) or (not b), andnot (a or b)equals(not a) and (not b).
Explain it without notes
Explain the difference between == and is, and when each is correct.
What is short-circuit evaluation? Give an example that would crash without it.
What does a or b return in Python, and why is name or "stranger" risky for numbers?
State De Morgan's laws and show an example in code.
Practice
Set age = 15 and print whether it is in the teenage range 13 to 19 (inclusive) with a chained comparison.
Set nickname = "" and print nickname if it has text, otherwise Guest, using or.
Set raining = True, has_umbrella = False. Print whether you get wet: you do if it rains and you have no umbrella.
Trade-offs
- ↔
**
ordefaults vs. explicit checks.**x or defaultis short but replaces every falsy value;default if x is None else x(Topic 2.2) is longer and exact. - ↔
Chained vs. spelled-out comparisons.
0 < x < 10is shorter and closer to maths. Spelled-out comparisons may be clearer to someone from another language, but are no more correct.
Done when you can
Done when you can use all six comparison operators and a chain.
Done when you can explain
==versusisand useis None.Done when you can predict what
orandandreturn for any two values.Done when you can explain short-circuiting with an example.
Done when you can rewrite
not (a and b)using De Morgan's law.