Embercode
✕
OperatorsLesson 3 of 14

50 min read

Python comparison and logical operators

Why this matters

Almost every decision in a program is a question with a yes or no answer. Is the password right? Has the basket reached the free-delivery limit? Is the number in the list at all? The code that answers these questions is built from comparison and logical operators, and the next module, on conditionals, uses their answers to choose what to run. If you cannot build a correct true-or-false question, the decision that depends on it will be wrong, and the bug can hide for a long time.

This lesson gives you the full toolkit. You will learn what the comparison symbols compare, how the logical words combine answers, how Python skips work it does not need, and how the precedence rules decide what a long condition means. The lesson ends with a palindrome check. It uses the two-pointer pattern, which is one of the most common patterns in coding interviews.

Python has two features that differ from many other languages. A chained comparison such as 1 < x < 10 is valid, and the logical words and and or return one of their operands instead of always returning a bool. Both are shown below.

Start from the first principle: a bool is an answer

A boolean is a type that holds one of two values: True or False. It is the simplest type in Python, and it is the answer to a yes-or-no question. The capital letter matters. true with a small letter is just an unknown name. You store a bool the same way as any other value:

Python · editable

The output is:

True
<class 'bool'>

A comparison operator compares two values and gives a bool. The comparison is a question, and the answer is used at once or stored. The six comparison operators are listed below.

OperatorQuestion it asksExampleAnswer
==are they equal?7 == 7True
!=are they not equal?7 != 7False
<is the left smaller?3 < 9True
>is the left larger?3 > 9False
<=is the left smaller or the same?9 <= 9True
>=is the left larger or the same?8 >= 9False

The most important detail is that == asks a question and = gives an instruction. The line x = 5 puts 5 into the name x. The line x == 5 looks at the name and says True or False. In the previous lesson you saw what happens when the two are confused inside an if. Python refuses that line with a syntax error.

Here is a complete program with a few comparisons. Predict each line before you run it.

Python · editable

The output is:

False
True
True
True

The last line joins two comparisons with and. Both must be true for the whole thing to be true. The next sections explain the word and in full.

Comparing text is not the same as comparing numbers

Numbers and text behave the same way under ==. Two strings are equal when they have the same characters, in the same order. The == operator compares values, so it is the one to use for text.

Python also has the operator is, which asks a different question. It asks whether two names refer to the same object in memory. The two questions have the same answer for small numbers and for many text values, which is why the difference is easy to miss. Look at this program:

Python · editable

The output is:

True True
True False

The name a and the name b refer to the same stored text, so a is b is True. Python stores identical text constants once, so this is the usual case. The name c is built at run time from a list of letters, so it refers to a different object. a is c is False, but the characters are the same, so a == c is True.

Use == whenever you compare the content of two values. Use is only for the value None, as in name is None, and for the few other cases where identity is the real question.

Logical operators combine answers

A logical operator combines booleans into one boolean. Python has three of them, and they are written as words:

OperatorNameTrue when
andANDboth sides are true
orORat least one side is true
notNOTthe one value is false (it flips the answer)

A simple way to remember them is to think about everyday rules. To enter a cinema you need a ticket and a valid ID, so both must be true. To pay, you can use cash or card, so one of them is enough. NOT reverses a rule: if the sign says "closed", the shop is not open.

A truth table lists every possible input and the answer for each. The program below prints the complete table for and and or. Each line reads as "left operator right equals result".

Python · editable

The output is:

True and True = True
True and False = False
False and True = False
False and False = False
True or True = True
True or False = True
False or True = True
False or False = False

The two loops run over the values True and False. Loops are the subject of a later module. For now, notice that the table is printed in the same order every time.

Read the table to find the rules. AND is true only in the first row, where both sides are true. OR is false only in the last row, where both sides are false. Those two facts are enough to answer most conditions.

A composed condition is built from smaller ones. Here is a condition for a student who passes. The student needs at least 40 marks and must have attended:

Python · editable

The output is:

True
False

The name passed is True, because both parts are true. When attended is False, the whole answer is False, however high the marks.

What and and or return

In many languages, and and or always give a bool. In Python they return one of their operands, which is the value that decided the answer. For a bool operand, that value is a bool. For other values, it may not be:

Python · editable

The output is:

default
fallback
[]
last
False False False True

The expression 0 or "default" gives "default", because 0 is false in a condition, so or moves on to the right side and returns it. The expression [] and 5 gives the empty list, because an empty list is false, so and stops at the left side. The function bool shows the truth value Python uses. The text "0" is true, because it is not empty.

This feature lets you write a default value in one line, such as name = user_name or "guest". It can surprise you when the value on the left is a number 0 or an empty string that you wanted to keep. When the result must be a bool, wrap the expression in bool(...) or compare with == and !=.

Truth tables for three inputs

A truth table lists every combination of its inputs and the answer for each. Two inputs give 4 rows, three inputs give 8 rows, and four inputs give 16 rows. The standard library function itertools.product builds every combination for you, so you never miss a row:

Python · editable

The output is:

a     b     c     a and b or c
True True True True
True True False True
True False True True
True False False False
False True True True
False True False False
False False True True
False False False False

The expression a and b or c ranks and above or, so it means (a and b) or c. Read two rows to see how the table works. In row 4, a and b disagree, so a and b is False, and c is False too, so the result is False. In row 3, the same disagreement happens, but c is True. The OR then gives True, whatever the AND gave. The c column alone decides the answer in every row where it is true.

Tables like this are the fastest way to check a complicated condition. Write the table once for the rule you want, then write the Python expression, then check each row against the output. If a row disagrees, you have found the bug without running the real program. The table is also the starting point for simplifying a condition, because two expressions are the same if their tables match row for row.

A discount rule, written in parts

Here is a realistic rule. A shop offers free delivery when the basket is worth at least 50, or when the customer has a coupon and the basket is worth at least 20. Written as a single line, the rule is hard to check. Written as named parts, it is simple:

Python · editable

The output is:

True

The first part, big_basket, asks the first question. The second part, coupon_ok, asks the second question, and it has its own and inside. The last line joins the two answers with or.

Trace three cases. For a total of 25 with a coupon, big_basket is False, and coupon_ok is True, because the coupon is held and 25 is at least 20. The OR is True, so delivery is free. For a total of 15 with a coupon, coupon_ok is False, because 15 is below 20, and big_basket is also False, so delivery is not free. For a total of 60 with no coupon, big_basket is True, and the answer is True, because the coupon is not needed.

The negation of the rule is useful too. The customer pays for delivery when free_delivery is False. By De Morgan's law, that is total < 50 and not coupon_ok. Check it with the same three cases: 25 with a coupon pays nothing, 15 with a coupon pays, and 60 without a coupon pays nothing. The negated form gives the same answers, as it should.

Naming the parts is the habit that makes this rule safe to change. If the shop later raises the coupon limit to 30, you change one line, and the rest of the reasoning stays visible.

Chained comparisons

Many conditions ask whether a value lies inside a range. In mathematics you might write 1 < x < 10. In Python that form is valid, and it means the same as two comparisons joined with and:

Python · editable

The output is:

True
True
False

Python reads 1 < x < 10 as "1 is less than x, and x is less than 10". The value x is used in both comparisons, and it is evaluated only once. Chained comparisons work with any comparison operators, such as 0 <= score <= 100.

Look at outside. It is the logical opposite of the range test. For any value, exactly one of the two is true. This is not a coincidence. It is an instance of De Morgan's laws, which are two rules about negation. The first says that not (a and b) is the same as not a or not b. The second says that not (a or b) is the same as not a and not b. In the example, not (1 < x < 10) becomes x <= 1 or x >= 10. Each comparison is flipped, and and becomes or.

De Morgan's laws matter in practice. A reader who sees not (logged_in and is_admin) can rewrite it as not logged_in or not is_admin, which often reads more naturally. Interviewers sometimes ask for a negated condition. Show that you can flip the operators inside as well as the outside.

Comparing characters and decimals

Characters follow the same comparison rules as numbers, because each character has a numeric code. The letter a has a smaller code than b, so 'a' < 'b' is True. Uppercase letters have smaller codes than lowercase letters, so 'Z' < 'a' is also True. A range test such as "a" <= ch <= "z" checks whether ch is a lowercase letter, and it works because the codes for the letters are in order. The strings module covers characters in detail.

Decimals have the approximation problem from the arithmetic lesson. Two float values that should be equal may differ in the last digits, so == can give False. Compare them with a tolerance, as the arithmetic lesson showed, with abs(a - b) < 0.000001 or math.isclose(a, b). The same warning applies to any comparison that depends on the result of a decimal calculation.

Short-circuit evaluation: skipping work you do not need

Now the most important idea in this lesson. The words and and or do not always look at both sides. Python reads the left side first. If that is enough to know the answer, Python skips the right side completely. This is called short-circuit evaluation.

Think about a bouncer at a club with two rules. The first rule is that you must be 18. The second rule is that you must have a ticket. If the bouncer sees that you are 15, there is no need to check your ticket. The answer for the whole entry is already no. Short-circuit evaluation works the same way.

  • left and right: if left is false, the answer is left, so right is never run.
  • left or right: if left is true, the answer is left, so right is never run.

The reason this matters is safety. A common pattern is to check a condition before doing something risky. Here is an example, using division:

Python · editable

The output is:

False

The left side x != 0 is False. So and stops there, and 10 / x is never evaluated. There is no division by zero, and safe is False. If you swapped the two sides, the division would run first and the program would stop with a ZeroDivisionError, as you saw in the arithmetic lesson.

The next program makes the skipped work visible. It uses a small helper function that prints its name before it returns an answer. Functions are the subject of a later module. For now, read check as a function that prints a label and gives back a bool:

Python · editable

The output is:

checking A
first: False
checking C
checking D
second: False
checking E
third: True

Look at the first answer. The and runs check("A", False), which returns False. Then and stops, so check("B", True) is never called. The message checking B never appears.

The second answer uses &, the single ampersand, instead of and. For bools, & is the operator for "both are true", but it does not short-circuit. It calls both sides, even when the left side is False. So both checking C and checking D appear. For plain bools the answer is the same as and, but the work is not. Use and and or in conditions, and reserve & and | for the bit operations covered in the next lesson.

The third answer uses or. The left side check("E", True) is True, so the right side is skipped. Only checking E appears.

Short-circuit evaluation is the reason a None check comes before a method call, as the section on None checks below shows. It is also the reason a bounds check comes before an index into a list, as the palindrome check below shows. In both cases the first condition protects the second.

Precedence: which operator goes first

Comparison and logical operators have their own ranks. From highest to lowest, the order you need is this:

  1. the arithmetic operators from the arithmetic lesson
  2. the comparison operators ==, !=, <, >, <=, >=, is and in
  3. not
  4. and
  5. or

So not ranks below the comparisons, and and ranks above or. That changes the meaning of a line with both. Predict each line before you run it:

Python · editable

The output is:

True
False
True
False

The first line is True or (False and False). The inner and is False, so the whole expression is True or False, which is True. The brackets in the second line force the or first, giving True and False, which is False.

The third line has no brackets. It reads as (5 > 3 and 2 > 4) or 1 < 2. The comparisons run first, giving (True and False) or True, which is True.

The fourth line has not (3 > 2), which is not True, which is False. The rest is False or (4 == 4 and 1 > 5). Inside, 4 == 4 is True and 1 > 5 is False, so the and is False. The whole line is False or False, which is False.

When you write a condition, do not rely on memorised precedence for the reader's sake. A bracket costs nothing and makes the meaning clear to anyone who reads it later.

A leap year: putting the rules together

The rule for a leap year is a good example of a condition built from several parts. A year is a leap year when it is divisible by 4, unless it is divisible by 100, unless it is also divisible by 400. Read as a sentence, that is hard to follow, so write it as three separate bool values:

Python · editable

The output is:

1900 is a leap year: False
2000 is a leap year: True
2024 is a leap year: True
2023 is a leap year: False

The three named bools make the logic readable. The line divisible_by_4 and not divisible_by_100 or divisible_by_400 follows the precedence rules. not runs first, then and, then or, so it means (divisible_by_4 and (not divisible_by_100)) or divisible_by_400. That is exactly the rule. Try 1900 and you get False, because 1900 is divisible by 100 but not by 400. Try 2024 and you get True.

Notice that year % 4 == 0 works without brackets. The remainder runs first, because it is an arithmetic operator, and then the equality test runs. You can rely on that order, but a bracket is still kinder to the reader.

Two pointers: a palindrome check

A palindrome is a word that reads the same backwards, such as racecar. To check a word, you can compare its first letter with its last, then its second with its second-last, and keep going toward the middle. You stop when the two ends meet, or when a pair differs. This is the two-pointer pattern: two positions move toward each other through the data.

The table below steps through racecar. Each row shows the two positions, the letters they point at, and what the check does next.

StepLeft position loRight position hiLettersResult of the check
106r and rmatch, move both inward
215a and amatch, move both inward
324c and cmatch, move both inward
433the same positionthe markers meet, stop

The pattern is an everyday example of a loop with a compound condition. The loops module teaches loops in full. For now, read while lo < hi and word[lo] == word[hi] as "keep repeating while this condition is true". The program below reads a word and reports whether it is a palindrome. It prints the markers at each step so you can see the table above happen.

Python · editable

The output is:

lo 0 hi 6 r r match
lo 1 hi 5 a a match
lo 2 hi 4 c c match
lo 3 hi 3 stop
racecar palindrome: True

The condition lo < hi and word[lo] == word[hi] depends on short-circuit evaluation. Suppose the word is empty. Then hi is -1 and lo is 0. The first part, lo < hi, is False, so word[lo] is never read. Without the short circuit, that read would fail on an empty string. The same protection stops the loop safely when the markers cross.

Now try hello. The first pair, h and o, differs, so the condition is False straight away, the loop does not run again, and lo >= hi is False. The output is hello palindrome: False. Run the same code with word = "hello" to check it yourself.

Python also has a short way to write the same check, which compares the word with its reverse. The slice word[::-1] makes a reversed copy. The two-pointer version does the same job without making the copy, so it is the one that interviewers expect you to write:

Python · editable

The output is:

'racecar' True True
'hello' False False
'' True True
'a' True True
'abba' True True
'abca' False False

The repr function shows the text with quotes, so the empty word is visible. The two columns agree for every word. The empty word and the single letter are palindromes, because there is no pair to disagree.

Two-pointer questions appear constantly in interviews. Reversing a list, finding a pair with a given sum in a sorted list and removing duplicates all use the same movement: start the markers at the ends, then move them based on what they see. You will meet these again in the arrays and sorting modules.

None checks and the order of conditions

Short-circuit evaluation also protects against a missing value. In Python, None means "no value". Calling a method on None stops the program with an AttributeError. Here is a program that tests the difference:

Python · editable

The first line prints False. The check name is not None is False, so name == "bob" is never run. The second line calls lower on None, which is a bug. The program stops with an exception, and the output is:

False
Traceback (most recent call last):
  File "/path/to/main.py", line 3, in <module>
    print(name.lower())
          ^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'lower'

The message tells you the method that failed, the type of the value, and the line. In your own code, the path in the File line is the file you saved. The type NoneType is the type of the value None.

The rule is simple. Put the cheap, safe check first, and put the risky call second. name is not None and name == "bob" is safe. The reverse order is not. The order of your conditions must protect the risky part.

Common mistakes and the messages you will see

Some mistakes with conditions give messages before the program runs. Others run silently and give the wrong answer. Learn both kinds.

Mistake 1: a comparison with no left side

Python allows chained comparisons, so the range check above is valid. A common mistake is to write the second half of a range without the variable:

Python · editable

The output is:

  File "/path/to/main.py", line 2
    print(1 < x and < 10)
                    ^
SyntaxError: invalid syntax

The operator < needs a value on its left. Write the variable again: 1 < x and x < 10, or use the chained form 1 < x < 10.

Mistake 2: an or with a bare value

Python · editable

The output is:

yes

No error is shown, and the program prints yes for x equal to 5. The expression x == 1 or 2 is False or 2, and 2 is a non-empty value, so it counts as true. The or needs a test on the right side too. The fix is x == 1 or x == 2, which asks the question twice in full. Writing the variable name each time looks repetitive, but it is the only correct form. This is one of the most common silent bugs in Python code.

Mistake 3: & where and was meant

For bools, & gives the same answer as and, but it does not short-circuit. It runs both sides every time. The example below divides by zero on the right side, so the difference shows at once:

Python · editable

The output is:

False
Traceback (most recent call last):
  File "/path/to/main.py", line 2, in <module>
    print(False & (1 / 0 == 1))
                   ~~^~~
ZeroDivisionError: division by zero

The first line prints False, because and stops at the left side. The second line fails, because & runs the division first. Use and and or for conditions.

Mistake 4: == on a value that might be None

The comparison name == None gives True for None, and it can give surprising results for objects that define their own ==. The idiom is name is None or name is not None, which checks the identity of the value. Use is only for None, and == for the content of values such as numbers and text.

Mistake 5: the wrong order of the protecting check

If the check that protects a risky call comes second, the risky call still runs first on the bad value:

Python · editable

The output is:

Traceback (most recent call last):
  File "/path/to/main.py", line 2, in <module>
    print(10 / x > 1 and x != 0)
          ~~~^~~
ZeroDivisionError: division by zero

Swapping the two parts, as in x != 0 and 10 / x > 1, prints False and does not crash. The order is the whole point.

Mistake 6: a mistyped = inside an if

In Python, = is not allowed inside an if test, so the mistake gives a syntax error:

Python · editable

The output is:

  File "/path/to/main.py", line 2
    if is_open = False:
       ^^^^^^^^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

Python also has the walrus operator :=, which assigns and gives the value at once. It is allowed in an if, so a mistyped := compiles without an error. The program below shows the silent case:

Python · editable

The output is:

False

The assignment gives False, which is a valid test for the if, so the block is skipped. The name is now permanently False. Python cannot tell that you meant ==. Read each condition and ask what it tests, and be suspicious of any := inside an if.

Debugging a condition that never matches

A realistic bug looks like this. A program should give free delivery when the basket total is at least 50 pounds, or when the customer has a coupon. The code is:

Python · editable

The output is:

False

The customer has a coupon, so the program should give free delivery, but it does not. The bug is the operator. The rule said "at least 50 or coupon", and the code wrote and, so both parts had to be true. The fix is:

Python · editable

The output is:

True

The method for finding this kind of bug is to write a truth table for the parts. The table has two rows of interest here: a basket of 45 with a coupon, and a basket of 60 without one. Check each row against the written rule. A single wrong operator shows up as a row with the wrong answer. Print the parts with separate lines while you debug, so you can see which part is False.

Variations and edge cases

Interviewers test conditions with edge cases, just as they test arithmetic. Here are the cases to check every time.

  • Boundaries. Test the value exactly at each limit. For marks >= 40, check 39, 40 and 41. Off-by-one errors happen at the boundary, not in the middle of a range.
  • Negative numbers. A range check such as 0 < x < 10 excludes zero and all negatives. Decide whether that is what you want.
  • Empty input. For an empty word, hi starts at -1, so check that the loop never reads a letter at position -1.
  • None. Any method call on a value that might be None needs a check first.
  • Equal values. <= and >= include the equal case. < and > do not. Many off-by-one bugs come from picking the wrong one.
  • Truthy values. and and or return operands, so a value such as 0, an empty string or an empty list can decide the answer without being a bool.

Interview-style patterns

Three kinds of condition questions come up often.

Compose from parts. Give the rule in words, write each part as a named bool, and combine them with and and or. The leap year example is the model. Naming the parts makes your reasoning visible to the interviewer, and it makes the final line easy to check.

Protect before you risk. When a condition uses division, an index or a method on a value that might be None, put the safe test first. Say that you are relying on short-circuit evaluation, because the interviewer wants to know that you know it.

Two-pointer scans. Any question that asks about pairs from both ends, such as a palindrome, a reversal or a sorted pair-sum, has a two-marker solution. The loop condition combines a bounds check with a comparison, and the short-circuit rule keeps the bounds check safe.

Reading a condition out loud

When a condition looks confusing, read it out loud in full sentences, then check each sentence against the operators. Take the condition age >= 18 and has_id or is_staff. Read it as "the person is an adult and has an ID, or the person is staff". That reading matches the brackets (age >= 18 and has_id) or is_staff, so the sentence tells you what the code must do.

Now test the reading with values. Suppose the person is 16, has no ID, and is staff. The sentence says "adult and has ID" is False, and "is staff" is True. The whole sentence is True. A reader who had assumed the other grouping, age >= 18 and (has_id or is_staff), would have said the person is not an adult, so the answer must be False. The two readings disagree on this input, and that is exactly why the brackets matter.

The program below checks both readings with the same values:

Python · editable

The output is:

True
False

This method finds the bug in a condition before any test runs. Write the sentence, write the brackets, then test one input that sits on the boundary of each part. A sentence that does not match the code is a bug in the code, and a code that does not match the sentence is a bug in the sentence. Fix whichever one is wrong.

A checklist for every condition

Use this list each time you write a condition. It takes a minute, and it catches most of the bugs in this lesson.

  • Say the question in plain words before you write any operator.
  • If the condition has more than two parts, name each part as a bool.
  • Check the operators: == for a test and = for a store, and and or for decisions, and & and | only for bits.
  • Put the safe check first, so the risky call is skipped when it would fail.
  • Test the boundary values, the zero or empty value, and the None value.
  • Add brackets whenever and and or appear together, even when precedence would give the same answer.
  • Remember that and and or return operands. Wrap the result in bool(...) when a plain bool is needed.

Practice

1. (Easy) Write a bool that is True when n is a positive even number.

Show the answer

n > 0 and n % 2 == 0. The first part excludes zero and negatives. The second part checks that the remainder is zero.

2. (Easy) Write a bool that is True when x is between 1 and 10, not including either end.

Show the answer

1 < x < 10, which is valid Python. The longer form 1 < x and x < 10 gives the same answer.

3. (Easy) Predict the output of print(not (3 > 2) or 4 == 4 and 1 > 5).

Show the answer

It prints False. not (3 > 2) is not True, which is False. Then 4 == 4 and 1 > 5 is True and False, which is False. Then False or False is False.

4. (Medium) Write a bool that is True when the year is divisible by 4 but not by 100. Use and and not.

Show the answer

year % 4 == 0 and year % 100 != 0. The first part is the divisibility test, and the second part excludes the years divisible by 100.

5. (Medium) Given x = 0, write a line that prints False without crashing for the expression 10 / x > 1.

Show the answer

Python · editable

The output is False. The left side is False, so the division is skipped.

6. (Medium) Predict the output of print(True or 1 / 0 == 1).

Show the answer

It prints True and does not crash. The left side is True, so the or is decided and the right side, which would divide by zero, is never run.

7. (Medium) A condition reads age >= 18 and has_id or is_staff. Add brackets that show what it means, then say what it returns when age is 16, has_id is True and is_staff is True.

Show the answer

With the precedence rules, it is (age >= 18 and has_id) or is_staff. Adding brackets makes that clear. For age 16, the left part is False. The right side is_staff is True, so the result is True.

8. (Medium) Rewrite not (logged_in and is_admin) without the outer not, using De Morgan's law.

Show the answer

not logged_in or not is_admin. Negating an and gives the or of the two negations.

9. (Hard) Explain in two sentences why name is not None and name.lower() == "bob" is safe and name.lower() == "bob" and name is not None is not, when name is None.

Show the answer

In the first form, the None test runs first. If name is None, the test is False, so the and stops, and name.lower() is never called. In the second form, name.lower() runs first on None and raises an AttributeError before the None test can help.

10. (Hard) Write a bool that checks whether index is a valid position in a list of size items, where valid means from 0 up to, but not including, size. Explain why the order of the two parts matters if the list is read afterwards.

Show the answer

0 <= index < size, or index >= 0 and index < size. Checking the lower bound first means that a negative index is rejected before any read that might use it. In a real read such as items[index] > 0, the bounds check must come before the read, and short-circuit evaluation guarantees that the read is skipped when the check fails.

11. (Hard) Using the two-pointer idea, write the loop condition that stops a search when the markers have crossed or when a pair differs, and explain which part is the safety check.

Show the answer

lo < hi and word[lo] == word[hi]. The part lo < hi is the safety check. It stops the loop before the markers pass each other, which would otherwise use positions outside the word. Short-circuit evaluation skips the character comparison whenever the markers have crossed.

What you now know

  • A bool is a yes-or-no answer. Comparisons such as ==, < and >= produce bools, and the values are written True and False.
  • == asks a question and = gives an instruction. == compares content, and is compares identity, which you use for None.
  • and is True only when both sides are true. or is True when either side is true. not flips a bool.
  • and and or return one of their operands, which can be a value that is not a bool.
  • and and or short-circuit: they skip the right side when the left side already decides the answer.
  • Python allows chained comparisons such as 1 < x < 10.
  • and ranks above or, so brackets are the best way to make a mixed condition clear.
  • Put the safe test first, so the risky call is skipped when it would fail.
  • De Morgan's laws let you flip a negated condition by changing the operators and the parts.
Practice this topic: coding problems for Operators →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Python assignment and augmented operators