Recommended Video Course
Python Booleans: Leveraging the Values of Truth
Table of Contents
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Python Booleans: Leveraging the Values of Truth
ThePython Boolean type is one of Python’sbuilt-in data types. It’s used to represent the truth value of an expression. For example, the expression1 <= 2
isTrue
, while the expression0 == 1
isFalse
. Understanding how Python Boolean values behave is important to programming well in Python.
In this tutorial, you’ll learn how to:
Free Bonus:5 Thoughts On Python Mastery, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.
The Python Boolean type has only two possible values:
True
False
No other value will havebool
as its type. You can check the type ofTrue
andFalse
with the built-intype()
:
>>>type(False)<class 'bool'>>>>type(True)<class 'bool'>
Thetype()
of bothFalse
andTrue
isbool
.
The typebool
isbuilt in, meaning it’s always available in Python and doesn’t need to be imported. However, the name itself isn’t a keyword in the language. While the following is considered bad style, it’s possible to assign to the namebool
:
>>>bool<class 'bool'>>>>bool="this is not a type">>>bool'this is not a type'
Although technically possible, to avoid confusion it’s highly recommended that you don’t assign a different value tobool
.
Built-in names aren’t keywords. As far as the Python language is concerned, they’re regularvariables. If you assign to them, then you’ll override the built-in value.
In contrast, the namesTrue
andFalse
arenot built-ins. They’rekeywords. Unlike many otherPython keywords,True
andFalse
are Pythonexpressions. Since they’re expressions, they can be used wherever other expressions, like1 + 1
, can be used.
It’s possible to assign a Boolean value to variables, but it’s not possible to assign a value toTrue
:
>>>a_true_alias=True>>>a_true_aliasTrue>>>True=5 File"<stdin>", line1SyntaxError:cannot assign to True
BecauseTrue
is a keyword, you can’t assign a value to it. The same rule applies toFalse
:
>>>False=5 File"<stdin>", line1SyntaxError:cannot assign to False
You can’t assign toFalse
because it’s a keyword in Python. In this way,True
andFalse
behave like other numeric constants. For example, you can pass1.5
to functions or assign it to variables. However, it’s impossible to assign a value to1.5
. The statement1.5 = 5
is not valid Python. Both1.5 = 5
andFalse = 5
are invalid Python code and will raise aSyntaxError
when parsed.
Booleans are considered anumeric type in Python. This means they’renumbers for all intents and purposes. In other words, you can apply arithmetic operations to Booleans, and you can also compare them to numbers:
>>>True==1True>>>False==0True>>>True+(False/True)1.0
There aren’t many uses for the numerical nature of Boolean values, but there’s one technique you may find helpful. BecauseTrue
is equal to1
andFalse
is equal to0
, adding Booleans together is a quick way to count the number ofTrue
values. This can come in handy when you need to count the number of items that satisfy a condition.
For example, if you want to analyze a verse in aclassic children’s poem to see what fraction of lines contain the word"the"
, then the fact thatTrue
is equal to1
andFalse
is equal to0
can come in quite handy:
>>>lines="""\...He took his vorpal sword in hand;... Long time the manxome foe he sought—...So rested he by the Tumtum tree... And stood awhile in thought....""".splitlines()>>>sum("the"inline.lower()forlineinlines)/len(lines)0.5
Summing all values in agenerator expression like this lets you know how many timesTrue
appears in the generator. The number of timesTrue
is in the generator is equal to the number of lines that contain the word"the"
, in a case-insensitive way. Dividing this number by the total number of lines gives you the ratio of matching lines to total lines.
To see why this works, you can break the above code into smaller parts:
>>>lines="""\...He took his vorpal sword in hand;... Long time the manxome foe he sought—...So rested he by the Tumtum tree... And stood awhile in thought....""">>>line_list=lines.splitlines()>>>"the"inline_list[0]False>>>"the"inline_list[1]True>>>0+False+True# Equivalent to 0 + 0 + 11>>>["the"inlineforlineinline_list][False, True, True, False]>>>False+True+True+False2>>>len(line_list)4>>>2/40.5
Theline_list
variable holds a list of lines. The first line doesn’t have the word"the"
in it, so"the" in line_list[0]
isFalse
. In the second line,"the"
does appear, so"the" in line_list[1]
isTrue
. Since Booleans are numbers, you can add them to numbers, and0 + False + True
gives1
.
Since["the" in line for line in line_list]
is a list of four Booleans, you can add them together. When you addFalse + True + True + False
, you get2
. Now, if you divide that result by4
, the length of the list, you get0.5
. The word"the"
appears in half the lines in the selection. This is a useful way to take advantage of the fact that Booleans are numbers.
Boolean operators are those that takeBoolean inputs and returnBoolean results.
Note: Later, you’ll see that these operators can be given other inputs and don’t always return Boolean results. For now, all examples will use Boolean inputs and results. You’ll see how this generalizes to other values in the section ontruthiness.
Since Python Boolean values have only two possible options,True
orFalse
, it’s possible to specify the operators completely in terms of the results they assign to every possible input combination. These specifications are calledtruth tables since they’re displayed in a table.
As you’ll see later, in some situations, knowing one input to an operator is enough to determine its value. In those cases, the other input isnot evaluated. This is calledshort-circuit evaluation.
The importance of short-circuit evaluation depends on the specific case. In some cases, it might have little effect on your program. In other cases, such as when it would be computationally intensive to evaluate expressions that don’t affect the result, it provides a significant performance benefit. In the most extreme cases, the correctness of your code can hinge on the short-circuit evaluation.
You can think ofTrue
andFalse
as Boolean operators that take no inputs. One of these operators always returnsTrue
, and the other always returnsFalse
.
Thinking of the Python Boolean values as operators is sometimes useful. For example, this approach helps to remind you that they’re not variables. For the same reason you can’t assign to+
, it’s impossible to assign toTrue
orFalse
.
Only two Python Boolean values exist. A Boolean operator with no inputs always returns the same value. Because of this,True
andFalse
are the only two Boolean operators that don’t take inputs.
not
Boolean OperatorThe only Boolean operator with one argument isnot
. It takes one argument and returns the opposite result:False
forTrue
andTrue
forFalse
. Here it is in a truth table:
A | not A |
---|---|
True | False |
False | True |
This table illustrates thatnot
returns the opposite truth value of the argument. Sincenot
takes only one argument, it doesn’t short-circuit. It evaluates its argument before returning its result:
>>>notTrueFalse>>>notFalseTrue>>>defprint_and_true():...print("I got called")...returnTrue...>>>notprint_and_true()I got calledFalse
The last line shows thatnot
evaluates its input before returningFalse
.
You might be wondering why there are no other Boolean operators that take a single argument. In order to understand why, you can look at a table that shows all theoretically possible Boolean operators that would take one argument:
A | not A | Identity | Yes | No |
---|---|---|---|---|
True | False | True | True | False |
False | True | False | True | False |
There are only four possible operators with one argument. Other thannot
, the remaining three operators all have somewhat whimsical names since they don’t actually exist:
Identity
: Since this operator simply returns its input, you could just delete it from your code with no effect.
Yes
: This is a short-circuit operator since it doesn’t depend on its argument. You could just replace it withTrue
and get the same result.
No
: This is another short-circuit operator since it doesn’t depend on its argument. You could just replace it withFalse
and get the same result.
None of the other possible operators with one argument would be useful.
and
Boolean OperatorTheand
operator takes two arguments. It evaluates toFalse
unless both inputs areTrue
. You could define the behavior ofand
with the following truth table:
A | B | A and B |
---|---|---|
True | True | True |
False | True | False |
True | False | False |
False | False | False |
This table is verbose. However, it illustrates the same behavior as the description above. IfA
isFalse
, then the value ofB
doesn’t matter. Because of this,and
short-circuits if the first input isFalse
. In other words, if the first input isFalse
, then the second input isn’t evaluated.
The following code has a second input that has aside effect, printing, in order to provide a concrete example:
>>>defprint_and_return(x):...print(f"I am returning{x}")...returnx...>>>Trueandprint_and_return(True)I am returning TrueTrue>>>Trueandprint_and_return(False)I am returning FalseFalse>>>Falseandprint_and_return(True)False>>>Falseandprint_and_return(False)False
In the last two cases, nothing is printed. The function isn’t called since calling it isn’t necessary to determine the value of theand
operator. Being aware of short-circuits is important when expressions have a side effect. In the last two examples, the short-circuit evaluation prevents the printing side effect from happening.
One example in which this behavior can be crucial is in code that might raise exceptions:
>>>definverse_and_true(n):...1//n...returnTrue...>>>inverse_and_true(5)True>>>inverse_and_true(0)Traceback (most recent call last): File"<stdin>", line1, in<module> File"<stdin>", line2, ininverse_and_trueZeroDivisionError:integer division or modulo by zero>>>Falseandinverse_and_true(0)False
The functioninverse_and_true()
is admittedly silly, and manylinters would warn about the expression1 // n
being useless. It does serve the purpose of neatly failing when given0
as a parameter since division by0
is invalid. However, the last line doesn’t raise an exception. Because of short-circuit evaluation, the function isn’t called, the division by0
doesn’t happen, and no exception is raised.
In contrast,True and inverse_and_true(0)
would raise an exception. In that case, the value of the second input would be needed for the result ofand
. Once the second input was evaluated,inverse_and_true(0)
would be called, it would divide by0
, and an exception would be raised.
or
Boolean OperatorThe value of theor
operator isTrue
unless both of its inputs areFalse
. Theor
operator could also be defined by the following truth table:
A | B | A or B |
---|---|---|
True | True | True |
False | True | True |
True | False | True |
False | False | False |
This table is verbose, but it has the same meaning as the explanation above.
When used informally, the wordor can have one of two meanings:
Theexclusiveor is howor is used in the phrase “You can file for an extension or submit your homework on time.” In this case, you can’t both file for an extension and submit your homework on time.
Theinclusiveor is sometimes indicated by using the conjunctionand/or. For example, “If you do well on this task, then you can get a raise and/or a promotion” means that you might get both a raise and a promotion.
When Python interprets the keywordor
, it does so using the inclusiveor. If both inputs areTrue
, then the result ofor
isTrue
.
Because it uses an inclusiveor, theor
operator in Python also uses short-circuit evaluation. If the first argument isTrue
, then the result isTrue
, and there is no need to evaluate the second argument. The following examples demonstrate the short-circuit evaluation ofor
:
>>>defprint_and_true():...print("print_and_true called")...returnTrue...>>>Trueorprint_and_true()True>>>Falseorprint_and_true()print_and_true calledTrue
The second input isn’t evaluated byor
unless the first one isFalse
. In practice, the short-circuit evaluation ofor
is used much less often than that ofand
. However, it’s important to keep this behavior in mind when reading code.
The mathematical theory of Boolean logic determines that no other operators beyondnot
,and
, andor
are needed. All other operators on two inputs can be specified in terms of these three operators. All operators on three or more inputs can be specified in terms of operators of two inputs.
In fact, even having bothor
andand
is redundant. Theand
operator can be defined in terms ofnot
andor
, and theor
operator can be defined in terms ofnot
andand
. However,and
andor
are so useful that all programming languages have both.
There are sixteen possible two-input Boolean operators. Except forand
andor
, they are rarely needed in practice. Because of this,True
,False
,not
,and
, andor
are the only built-in Python Boolean operators.
Some ofPython’s operators check whether a relationship holds between two objects. Since the relationship either holds or doesn’t hold, these operators, calledcomparison operators, always return Boolean values.
Comparison operators are the most common source of Boolean values.
The most common comparison operators are theequality operator (==
) and theinequality operator (!=
). It’s almost impossible to write any meaningful amount of Python code without using at least one of those operators.
The equality operator (==
) is one of the most used operators in Python code. You often need to compare either an unknown result with a known result or two unknown results against each other. Some functions return values that need to be compared against asentinel to see if some edge condition has been detected. Sometimes you need to compare the results from two functions against each other.
The equality operator is often used to compare numbers:
>>>1==1True>>>1==1.0True>>>1==2False
You may have usedequality operators before. They’re some of the most common operators in Python. For all built-in Python objects, and for most third-party classes, they return aBoolean value:True
orFalse
.
Note: The Python language doesn’t enforce that==
and!=
return Booleans. Libraries likeNumPy andpandas return other values.
Second only to the equality operator in popularity is theinequality operator (!=
). It returnsTrue
if the arguments aren’t equal andFalse
if they are. The examples are similarly wide-ranging. Manyunit tests check that the value isn’t equal to a specific invalid value. A web client might check that the error code isn’t404 Not Found
before trying an alternative.
Here are two examples of the Python inequality operator in use:
>>>1!=2True>>>1!=(1+0.0)False
Perhaps the most surprising thing about the Python inequality operator is the fact that it exists in the first place. After all, you could achieve the same result as1 != 2
withnot (1 == 2)
. Python usually avoids extra syntax, and especially extra core operators, for things easily achievable by other means.
However, inequality is used so often that it was deemed worthwhile to have a dedicated operator for it. In old versions of Python, in the1.x
series, there were actuallytwo different syntaxes.
As an April Fools’ joke, Python still supports an alternative syntax for inequality with the right__future__
import:
>>>from__future__importbarry_as_FLUFL>>>1<>2True
Thisshould never be used in any code meant for real use. It could come in handy for your next Python trivia night, however.
Another set of test operators are theorder comparison operators. There are four order comparison operators that can be categorized by two qualities:
Since the two choices are independent, you get2 * 2 == 4
order comparison operators. All four are listed in this table:
Less than | Greater than | |
---|---|---|
Strict | < | > |
Not strict | <= | >= |
There are two options for direction and two options for strictness. This results in total of four order comparison operators.
The order comparison operators aren’t defined for all objects. Some objects don’t have a meaningful order. Even thoughlists and tuples are orderedlexicographically,dictionaries don’t have a meaningful order:
>>>{1:3}<{2:4}Traceback (most recent call last): File"<stdin>", line1, in<module>TypeError:'<' not supported between instances of 'dict' and 'dict'
It’s not obvious how dictionaries should be ordered. As per theZen of Python, in the face of ambiguity, Python refuses to guess.
Whilestrings andintegers are ordered separately, intertype comparisons aren’t supported:
>>>1<="1"Traceback (most recent call last): File"<stdin>", line1, in<module>TypeError:'<=' not supported between instances of 'int' and 'str'
Again, since there’s no obvious way to define order, Python refuses to compare them. This is similar to the addition operator (+
). Though you can add strings to strings and integers to integers, adding strings to integers raises an exception.
When the order comparison operatorsare defined, in general they return a Boolean.
Note: Python doesn’t enforce that comparison operators return Booleans. While all built-in Python objects, and most third-party objects, return Booleans when compared, there are exceptions.
For example, comparison operators between NumPy arrays orpandas DataFrames return arrays and DataFrames. You’ll see more about the interaction of NumPy and Boolean values later in this tutorial.
Comparing numbers in Python is a common way of checking against boundary conditions. Note that<
doesn’t allow equality, while<=
does:
>>>1<=1True>>>1<1False>>>2>3False>>>2>=2True
Programmers often use comparison operators without realizing that they return a Python Boolean value.
is
OperatorTheis
operator checks forobject identity. In other words,x is y
evaluates toTrue
only whenx
andy
evaluate to the same object. Theis
operator has an opposite, theis not
operator.
A typical usage ofis
andis not
is to compare lists for identity:
>>>x=[]>>>y=[]>>>xisxTrue>>>xisnotxFalse>>>xisyFalse>>>xisnotyTrue
Even thoughx == y
, they are not the same object. Theis not
operator always returns the opposite ofis
. There’s no difference between the expressionx is not y
and the expressionnot (x is y)
except for readability.
Keep in mind that the above examples show theis
operator used only with lists. The behavior of theis
operator onimmutable objects like numbers and strings ismore complicated.
in
OperatorThein
operator checks formembership. An object can define what it considers members. Most sequences, such as lists, consider their elements to be members:
>>>small_even=[2,4]>>>1insmall_evenFalse>>>2insmall_evenTrue>>>10insmall_evenFalse
Since2
is an element of the list,2 in small_even
returnsTrue
. Since1
and10
aren’t in the list, the other expressions returnFalse
. In all cases, thein
operator returns a Boolean value.
Since strings are sequences of characters, you might expect them to also check for membership. In other words, characters that are members of the string will returnTrue
forin
, while those that don’t will returnFalse
:
>>>"e"in"hello beautiful world"True>>>"x"in"hello beautiful world"False
Since"e"
is the second element of the string, the first example returnsTrue
. Sincex
doesn’t appear in the string, the second example returnsFalse
. However, along with individual characters, substrings are also considered to be members of a string:
>>>"beautiful"in"hello beautiful world"True>>>"belle"in"hello beautiful world"False
Since"beautiful"
is a substring, thein
operator returnsTrue
. Since"belle"
is not a substring, thein
operator returnsFalse
. This is despite the fact that every individual letter in"belle"
is a member of the string.
Like the operatorsis
and==
, thein
operator also has an opposite,not in
. You can usenot in
to confirm that an element is not a member of an object.
Comparison operators can formchains. You can create comparison operator chains by separating expressions with comparison operators to form a larger expression:
>>>1<2<3True
The expression1 < 2 < 3
is a comparison operator chain. It has expressions separated by comparison operators. The result isTrue
because both parts of the chain areTrue
. You can break up the chain to see how it works:
>>>1<2and2<3True
Since1 < 2
returnsTrue
and2 < 3
returnsTrue
,and
returnsTrue
. A comparison chain is equivalent to usingand
on all its links. In this case, sinceTrue and True
returnsTrue
, the result of the whole chain isTrue
. This means that if any of the links areFalse
, then the whole chain isFalse
:
>>>1<3<2False
This comparison chain returnsFalse
since not all of its links areTrue
. Because comparison chains are an implicitand
operator, if even one link isFalse
, then the whole chain isFalse
. You can break up the chain to see how it works:
>>>1<3and3<2False
In this case, the parts of the chain evaluate to the following Booleans:
1 < 3
isTrue
3 < 2
isFalse
This means that one of the results isTrue
and one isFalse
. SinceTrue and False
is equal toFalse
, the value of the entire chain isFalse
.
You can mix types and operations in a comparison chain as long as the types can be compared:
>>>1<2<1False>>>1==1.0<0.5False>>>1==1.0==TrueTrue>>>1<3>2True>>>1<2<3<4<5True
The operators don’t have to be all the same. Not even the types have to be all the same. In the examples above, you have three numeric types:
int
float
bool
These are three different numeric types, but you can compare objects of different numeric types without issue.
If chains use an implicitand
, then chains must also short-circuit. This is important because even in cases where an order comparison isn’t defined, it’s possible for a chain to returnFalse
:
>>>2<"2"Traceback (most recent call last): File"<stdin>", line1, in<module>TypeError:'<' not supported between instances of 'int' and 'str'>>>3<2<"2"False
Even though Python can’t order-compare integers and strings numbers,3 < 2 < "2"
evaluates toFalse
because it doesn’t evaluate the second comparison. In this case, the short-circuit evaluation prevents another side effect: raising an exception.
Short-circuit evaluation of comparison chains can prevent other exceptions:
>>>3<2<(1//0)False
Dividing1
by0
would have raised aZeroDivisionError
. However, because of the short-circuit evaluation, Python doesn’t evaluate the invalid division. This means that Python skips evaluating not only the comparison but also the inputs to the comparison.
Another aspect that is important to understand about comparison chains is that when Pythondoes evaluate an element in the chain, it evaluates it only once:
>>>deffoo():...print("I'm foo")...return1...>>>0<foo()<2I'm fooTrue>>>(0<foo())and(foo()<2)I'm fooI'm fooTrue
Because the middle elements are evaluated only once, it’s not always safe to refactorx < y < z
to(x < y) and (y < z)
. Although the chain behaves likeand
in its short-circuit evaluation, it evaluates all values, including the intermediate ones, only once.
Chains are especially useful forrange checks, which confirm that a value falls within a given range. For example, in a daily invoice that includes the number hours worked, you might do the following:
>>>hours_worked=5>>>1<=hours_worked<=25True
If there are0
hours worked, then there’s no reason to send the invoice. Accounting for Daylight Saving Time, the maximum number of hours in a day is25
. The above range check confirms that the number of hours worked in a day falls within the allowable range.
Until now, all our examples involved==
,!=
, and the order comparisons. However, you can chain all of Python’s comparison operators. This can lead to surprising behavior:
>>>a=0>>>aisa<1True>>>(aisa)<1False>>>ais(a<1)False
Becausea is a < 1
is a comparison chain, it evaluates toTrue
. You can break the chain into its parts:
a is a
isTrue
, as it would be for any value evaluated against itself.a < 1
isTrue
since0
is less than1
.Since both parts areTrue
, the chain evaluates toTrue
.
However, people who are used to other operators in Python may assume that, like other expressions involving multiple operators such as1 + 2 * 3
, Python inserts parentheses into to the expression. However, neither way of inserting parenthesis will evaluate toTrue
.
You can see why both evaluate toFalse
if you break up the expressions. If you break up the first expression, you get the following:
>>>a=0>>>aisaTrue>>>True==1True>>>(aisa)<1False
You can see above thata is a
returnsTrue
, as it would for any value. This means that(a is a) < 1
is the same asTrue < 1
. Booleans are numeric types, andTrue
is equal to1
. SoTrue < 1
is the same as1 < 1
. Since this is astrict inequality, and1 == 1
, it returns False.
The second expression works differently:
>>>a=0False>>>a<1True>>>0isTrue<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?False
Since0
is less than1
,a < 1
returnsTrue
. Since0 != True
, then it can’t be the case that0 is True
.
Note: Don’t take the aboveSyntaxWarning
lightly. Usingis
on numbers can beconfusing. However, specifically for cases in which you know the numbers arenot equal, you can know thatis
will also returnFalse
. While this example is correct, it’s not an example of good Python coding style.
The most important lesson to draw from this is that chaining comparisons withis
usually isn’t a good idea. It confuses the reader and probably isn’t necessary.
Likeis
, thein
operator and its opposite,not in
, can often yield surprising results when chained:
>>>"b"in"aba"in"cabad"<"cabae"True
To maximize the confusion, this example chains comparisons with different operators and usesin
with strings to check for substrings. Again, this is not an example of well-written code! However, it’s important to be able to read this example and understand why it returnsTrue
.
Finally, you can chainis not
withnot in
:
>>>greeting="hello">>>quality="good">>>end_greeting="farewell">>>greetingisnotqualitynotinend_greetingTrue
Note that the order ofnot
in the two operators isn’t the same! The negative operators areis not
andnot in
. This corresponds with the regular usage in English, but it’s easy to make a mistake when modifying code.
The most popular use for a Python Boolean is in anif
statement. This statement will execute if the value isTrue
:
>>>1==1True>>>if1==1:...print("yep")...yep>>>1==2False>>>if1==2:...print("yep")...
print()
is called only when the expression evaluates toTrue
. However, in Python you can give any value toif
. The values thatif
considersTrue
are calledtruthy, and the values thatif
considersFalse
are calledfalsy.
if
decides which values are truthy and which are falsy by internally calling the built-inbool()
. You’ve already encounteredbool()
as the Python Boolean type. When called, it converts objects to Booleans.
None
as a Boolean ValueThe singleton objectNone
is always falsy:
>>>bool(None)False
This is often useful inif
statements that check for a sentinel value. However, it’s usually better to explicitly check for identity withis None
. SometimesNone
can be useful in combination with short-circuit evaluation in order to have a default.
For example, you can useor
to substituteNone
with an empty list:
>>>defadd_num_and_len(num,things=None):...returnnum+len(thingsor[])...>>>add_num_and_len(5,[1,2,3])8>>>add_num_and_len(6)6
In this example, the list won’t be created ifthings
is a non-empty list sinceor
will short-circuit before it evaluates[]
.
For numbers,bool(x)
is equivalent tox != 0
. This means the only falsy integer is0
:
>>>bool(3),bool(-5),bool(0)(True, True, False)
All nonzero integers are truthy. This is also true forfloating-point numbers, including special floating-point numbers likeinfinity andNot a Number (NaN):
>>>importmath>>>[bool(x)forxin[0,1.2,0.5,math.inf,math.nan]][False, True, True, True, True]
Since infinity and NaN aren’t equal to0
, they’re truthy.
Equality and inequality comparisons on floating-point numbers are subtle operations. Since doingbool(x)
is equivalent tox != 0
, this can lead to surprising results for floating-point numbers:
>>>bool(0.1+0.2+(-0.2)+(-0.1))True>>>0.1+0.2+(-0.2)+(-0.1)2.7755575615628914e-17
Floating-point number computations can be inexact. Because of that, the results ofbool()
on floating-point numbers can be surprising.
Python has more numeric types in the standard library, and they follow the same rules. For non-built-in numeric types,bool(x)
is also equivalent tox != 0
. Thefractions
module is in the standard library. Like other numeric types, the only falsy fraction is0/1
:
>>>importfractions>>>bool(fractions.Fraction("1/2")),bool(fractions.Fraction("0/1"))(True, False)
As with integers and floating-point numbers, fractions are false only when they’re equal to0
.
Thedecimal
module is also in the standard library. Decimals are similarly falsy only when they’re equal to0
:
>>>importdecimal,math>>>withdecimal.localcontext(decimal.Context(prec=3))asctx:...bool(ctx.create_decimal(math.pi)-ctx.create_decimal(22)/7)...False>>>withdecimal.localcontext(decimal.Context(prec=4))asctx:...bool(ctx.create_decimal(math.pi)-ctx.create_decimal(22)/7)...True
The number22 / 7
is an approximation of Pi to two decimal places. This fact was discussed byArchimedes in the 3rd century BCE. When the difference between22 / 7
and Pi is computed with this precision, the result is falsy. When the difference is computed with higher precision, the difference isn’t equal to0
, and so is truthy.
In general, objects that have alen()
will be falsy when the result oflen()
is0
. It doesn’t matter if they’re lists, tuples, sets, strings, or byte strings:
>>>bool([1]),bool([])(True, False)>>>bool((1,2)),bool(())(True, False)>>>bool({1,2,3}),bool(set())(True, False)>>>bool({1:2}),bool({})(True, False)>>>bool("hello"),bool("")(True, False)>>>bool(b"xyz"),bool(b"")(True, False)
All built-in Python objects that have a length follow this rule. Later, you’ll see some exceptions to this rule for non-built-in objects.
Unless types have alen()
or specifically define whether they’re truthy or falsy, they’re always truthy. This is true for built-in as well as user-defined types. In particular, functions are always truthy:
>>>deffunc():...pass...>>>bool(func)True
Methods are always truthy, too. You might encounter this if a parenthesis is missing when you call a function or method:
>>>importdatetime>>>defbefore_noon():...returndatetime.datetime.now().hour<12...>>>defgreet():...ifbefore_noon:...print("Good morning!")...else:...print("Good evening!")...>>>greet()Good morning!>>>datetime.datetime.now().hour20
This can happen as a result of a forgotten parenthesis or misleading documentation that doesn’t mention that you need to call the function. If you expect a Python Boolean value but have a function that returns a Boolean value, then it will always be truthy.
By default, user-defined types are always truthy:
>>>classDummy:...pass...>>>bool(Dummy())True
Creating an empty class makes every object of that class truthy. All objects are truthy unlessspecial methods are defined. If you want to make some instances of your class falsy, you can define.__bool__()
:
>>>classBoolLike:...am_i_truthy=False...def__bool__(self):...returnself.am_i_truthy...>>>x=BoolLike()>>>bool(x)False>>>x.am_i_truthy=True>>>bool(x)True
You can also use.__bool__()
to make an object neither truthy nor falsy:
>>>classExcludedMiddle:...def__bool__(self):...raiseValueError("neither")...>>>x=ExcludedMiddle()>>>bool(x)Traceback (most recent call last): File"<stdin>", line1, in<module> File"<stdin>", line3, in__bool__ValueError:neither>>>ifx:...print("x is truthy")...else:...print("x is falsy")...Traceback (most recent call last): File"<stdin>", line1, in<module> File"<stdin>", line3, in__bool__ValueError:neither
Theif
statement also uses.__bool__()
. It does so to evaluate whether the object is truthy or falsy, which determines which branch to execute.
If you define the__len__
method on a class, then its instances have alen()
. In that case, the Boolean value of the instances will be falsy exactly when their length is0
:
>>>classDummyContainer:...my_length=0...def__len__(self):...returnself.my_length...>>>x=DummyContainer()>>>bool(x)False>>>x.my_length=5>>>bool(x)True
In this example,len(x)
would return0
before the assignment and5
afterward. The reverse, however, is not true. Defining.__bool__()
doesn’t give instances a length:
>>>classAlwaysTrue:...def__bool__(self):...returnTrue...>>>classAlwaysFalse:...def__bool__(self):...returnFalse...>>>bool(AlwaysTrue()),bool(AlwaysFalse())(True, False)>>>len(AlwaysTrue())Traceback (most recent call last): File"<stdin>", line1, in<module>TypeError:object of type 'AlwaysTrue' has no len()>>>len(AlwaysFalse())Traceback (most recent call last): File"<stdin>", line1, in<module>TypeError:object of type 'AlwaysFalse' has no len()
Defining.__bool__()
doesn’t make instances of either class have alen()
. When both.__bool__()
and.__len__()
are defined,.__bool__()
takes precedence:
>>>classBooleanContainer:...def__len__(self):...return100...def__bool__(self):...returnFalse...>>>x=BooleanContainer()>>>len(x)100>>>bool(x)False
Even thoughx
has a length of100
, it’s still falsy.
The above example may seem like something that only happens when you write a class intended to demonstrate edge cases in Python. However, it’s possible to get similar results using one of the most popular libraries onPyPI:NumPy.
Arrays, like numbers, are falsy or truthy depending on how they compare to0
:
>>>fromnumpyimportarray>>>x=array([0])>>>len(x)1>>>bool(x)False
Even thoughx
has a length of1
, it’s still falsy because its value is0
.
When arrays have more than one element, some elements might be falsy and some might be truthy. In those cases, NumPy will raise an exception:
>>>fromnumpyimportarray>>>importtextwrap>>>y=array([0,1])>>>try:...bool(y)...exceptValueErrorasexc:...print("\n".join(textwrap.wrap(str(exc))))...The truth value of an array with more than one element is ambiguous.Use a.any() or a.all()
The exception is so wordy that in order to make it easy to read, the code uses text processing to wrap the lines.
An even more interesting edge case involves empty arrays. You might wonder if those are falsy like other sequences or truthy because they’re not equal to0
. As you saw above, those aren’t the only two possible answers. The arrays could also refuse to have a Boolean value.
Interestingly, none of these options is entirely true:
>>>bool(array([]))<stdin>:1: DeprecationWarning: The truth value of an empty array is ambiguous.Returning False, but in future this will result in an error.Use `array.size > 0` to check that an array is not empty.False
While empty arrays arecurrently falsy, relying on this behavior is dangerous. In some future NumPy version, this will raise an exception.
There are a few more places in Python where Boolean testing takes place. One of those is in Boolean operators.
The operatorsand
,or
, andnot
accept any value that supports Boolean testing. In the case ofnot
, it will always return a Boolean value:
>>>not1False>>>not0True
The truth table fornot
is still correct, but now it takes the truthiness of the input.
In the case ofand
andor
, in addition to short-circuit evaluation, they also return the value at which they stopped evaluating:
>>>1and22>>>0and10>>>1or21>>>0or22
The truth tables are still correct, but they now define the truthiness of the results, which depends on the truthiness of the inputs. This can come handy when, for example, you want to give values defaults.
Assume you have a function calledsummarize()
that, if the text is too long, takes the beginning and the end and adds an ellipsis (...
) in the middle. This might be useful in some reports that can’t fit the full text. However, some datasets have missing values represented byNone
.
Sincesummarize()
assumes the input is a string, it will fail onNone
:
>>>defsummarize(long_text):...iflen(long_text)<=4:...returnlong_text...returnlong_text[:2]+"..."+long_text[-2:]...>>>summarize("hello world")'he...ld'>>>summarize("hi")'hi'>>>summarize("")''>>>summarize(None)Traceback (most recent call last): File"<stdin>", line1, in<module> File"<stdin>", line2, insummarizeTypeError:object of type 'NoneType' has no len()>>>forain["hello world","hi","",None]:...print("-->",summarize(aor""))...--> he...ld--> hi-->-->
This example takes advantage of the falsiness ofNone
and the fact thator
not only short-circuits but also returns the last value to be evaluated. The code for printing the report addsor ""
to the argument tosummarize()
. The addition ofor ""
helps you to avoid errors with just a small code change.
The built-in functionsall()
andany()
evaluate truthiness and also short-circuit, but they don’t return the last value to be evaluated.all()
checks whether all of its arguments are truthy:
>>>all([1,2,3])True>>>all([0,1,2])False>>>all(x/(x-1)forxin[0,1])False
In the last line,all()
doesn’t evaluatex / (x - 1)
for1
. Since1 - 1
is0
, this would have raised aZeroDivisionError
.
any()
checks whether any of its arguments are truthy:
>>>any([1,0,0])True>>>any([False,0,0.0])False>>>any(1/xforxin[1,0])True
In the last line,any()
doesn’t evaluate1 / x
for0
.
The Python Boolean is a commonly used data type with many useful applications. You can use Booleans with operators likenot
,and
,or
,in
,is
,==
, and!=
to compare values and check for membership, identity, or equality. You can also use Boolean testing with anif
statement to control the flow of your programs based on the truthiness of an expression.
In this tutorial, you’ve learned how to:
You now know how short-circuit evaluation works and recognize the connection between Booleans and theif
statement. This knowledge will help you to both understand existing code and avoid common pitfalls that can lead to errors in your own programs.
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Python Booleans: Leveraging the Values of Truth
🐍 Python Tricks 💌
Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.
AboutMoshe Zadka
Moshe has been using Python since 1998. He has contributed to CPython, and is a founding member of the Twisted project. He has been teaching Python in various venues since 2002.
» More about MosheMasterReal-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
MasterReal-World Python Skills
With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
What Do You Think?
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.
Keep Learning
Related Topics:intermediatepython
Recommended Video Course:Python Booleans: Leveraging the Values of Truth
Related Tutorials:
Already have an account?Sign-In
Almost there! Complete this form and click the button below to gain instant access:
5 Thoughts On Python Mastery