Abstract¶
Conditional execution allows a program to selectively execute different parts of the code based on specific conditions. This capability enables the program to adapt and respond appropriately to various situations it encounters, making it more flexible and functional. By using boolean expressions and conditional statements, students will learn to direct the flow of execution to handle a wide range of inputs and scenarios effectively.
import math
from flowcharts import *
from ipywidgets import interact
from dis import disMotivation¶
Conditional execution means running different pieces of code based on different conditions. Why do programmers need conditional execution?
For instance, when trying to compute a/b, b may be 0 and division by 0 is invalid.
%%pytutor
def multiply_or_divide(a, b):
print("a:{}, b:{}, a*b:{}, a/b:{}".format(a, b, a * b, a / b))
multiply_or_divide(1, 2)
multiply_or_divide(1, 0)Although division by 0 is invalid, multiplication remains valid but it is not printed due to the division error. Can we skip only the division but not the multiplication when b is equal to 0?
Solution 1: Use a conditional expression that specifies which code block should be executed under what condition:
... if ... else ...def multiply_or_divide(a, b):
q = a / b if b else "undefined"
print("a:{}, b:{}, a*b:{}, a/b:{}".format(a, b, a * b, q))
multiply_or_divide(1, 2)
multiply_or_divide(1, 0) # multiplication is valid but not showna:1, b:2, a*b:2, a/b:0.5
a:1, b:0, a*b:0, a/b:undefined
Solution 2: Use a boolean expression:
... and ... or ...def multiply_or_divide(a, b):
q = b and a / b or "undefined"
print("a:{}, b:{}, a*b:{}, a/b:{}".format(a, b, a * b, q))
multiply_or_divide(1, 2)
multiply_or_divide(1, 0) # multiplication is valid but not showna:1, b:2, a*b:2, a/b:0.5
a:1, b:0, a*b:0, a/b:undefined
Solution 3: Monitor and catch the error using a try statement:
def multiply_or_divide(a, b):
try:
q = a / b
except ZeroDivisionError:
q = "undefined"
print("a:{}, b:{}, a*b:{}, a/b:{}".format(a, b, a * b, q))
multiply_or_divide(1, 2)
multiply_or_divide(1, 0) # multiplication is valid but not showna:1, b:2, a*b:2, a/b:0.5
a:1, b:0, a*b:0, a/b:undefined
In this notebook, we will introduce the first two solutions but the last one can be a better solution for more complicated scenarios.
%%hermes
Explain in one paragraph, using a simple example, the benefits of using a
try statement over conditional checks for handling exceptions and errors.Comparison Operators¶
A comparison/relational operator along with its operands form an expression, which evaluates to a boolean value:
True # if the operands satisfy certain conditions, andTrueFalse # otherwise.FalseUnlike many other languages, Python capitalized the keywords for the boolean
values to signify that they are constants, just like the keyword None. Hence,
true = False # is invalid in many languages but not Python# False = true # is valid in many languages but not PythonWhy are boolean values “boolean”?
Boolean expressions are named after George Boole, whose work laid the foundation of digital circuitry. Below are the links to the ebooks, thanks to Project Gutenberg:
An important result derived from Boole’s framework for digital circuits is Boole’s/Shannon expansion theorem.
%%hermes
Explain Entscheidungsproblem and its relation to George Boole.↻ Session: main (ACP: ec63f1db-2c0…)
See Also
The Entscheidungsproblem (German: “decision problem”) was posed by David Hilbert in 1928, formally stated in Hilbert and Ackermann’s book Principles of Mathematical Logic. It asked:
Can every well-formed mathematical statement be proven true or false algorithmically?
In other words: is there a general mechanical procedure that, given any mathematical statement, can always decide whether it is true or false?
Turing proved the answer is NO — in his 1936 paper, he showed no such algorithm can exist. This is one of the foundational results of computer science: there are fundamental limits to what any computational process can decide.
The connection runs through the chain of ideas in the notes:
George Boole (1815–1864) created Boolean algebra — the system where logic is arithmetic. Truth values are just numbers (TRUE = 1, FALSE = 0), and logical operations (AND, OR, NOT) are arithmetic operations. His key insight: logic can be computed.
Hilbert’s programme (early 20th century) asked whether this algebraic approach could be made complete: can all true mathematical statements be proven? And is there an algorithm to decide truth? — the Entscheidungsproblem.
Turing’s negative answer to the Entscheidungsproblem showed that despite Boole’s beautiful reduction of logic to algebra, there are mathematical truths that no algorithm — no matter how clever — can decide.
The irony, as the notes put it: “Logic is arithmetic. Arithmetic has no general answer machine.” Boole showed logic could be computed, but Turing showed computation has inherent limits — some questions are fundamentally undecidable.
Equality and Inequalities¶
The equality and inequality relationships in mathematics are implemented using the following comparison operators:
You can explore these operators using the widget below:
comparison_operators = ["==", "<", "<=", ">", ">=", "!="]
@interact(operand1="10", operator=comparison_operators, operand2="3")
def comparison(operand1, operator, operand2):
expression = f"{operand1} {operator} {operand2}"
value = eval(expression)
print(
f"""{'Expression:':>11} {expression}\n{'Value:':>11}
{value}\n{'Type:':>11} {type(value)}"""
)%%hermes
Explain very briefly how the comparison operators == may be represented
differently in other programming languages such as maxima, scheme, and bash.↻ Session: main (ACP: ec63f1db-2c0…)
See Also
In Python, == tests equality. Other languages use different syntax:
| Language | Equality operator | Example |
|---|---|---|
| Maxima | = | 3 = 3 → true |
| Scheme | =, eq?, equal? | (= 3 3) — note prefix notation |
| Bash | =, == (in [[ ]]), -eq (numeric) | [ "$a" = "$b" ] or [[ $a == $b ]] |
Key difference: Maxima uses single = for comparison (not assignment — that’s :). Scheme uses prefix form and has multiple equality predicates depending on what you’re comparing. Bash treats =/== as string comparison only, requiring separate -eq for integers.
What is the precedence of the comparison operators?
All the comparison operators have the same precedence lower than that of + and -.
1 + 2 >= 3 # (1 + 2) >= 3TrueSimilar to the assignment operations, comparison operators can be chained together but they are non-associative.
2.0 == 2 > 1, (2.0 == 2) > 1, 2.0 == (2 > 1)(True, False, False)1 <= 2 < 3 != 4, (1 <= 2) < (3 != 4)(True, False)Solution to Exercise 1 #
The second expression is not a chained comparison:
The expressions in the parentheses are evaluated to boolean values first to
True, and sothe overall expression
True < Trueis evaluated toFalse. You may also want to try:False < True- True ** 2 + 11 / False
To understand how bool is related to int, see the PEP 285.
# Comparisons beyond numbers
@interact(
expression=[
"10 == 10.",
'"A" == "A"',
'"A" == "A "',
'"A" != "a"',
'"A" > "a"',
'"aBcd" < "abd"',
'"A" != 64',
'"A" < 64',
]
)
def relational_expression(expression):
print(eval(expression))Solution to Exercise 2 #
Checks whether an integer is equal to a floating point number.
Checks whether two characters are the same.
Checks whether two strings are the same. Note the space character.
Checks whether a character is larger than the order character according to their unicodes.
Checks whether a string is lexicographically smaller than the other string.
Checks whether a character is not equal to an integer.
Raises
TypeErrorbecause there is no implementation that evaluates whether a string is smaller than an integer.
Comparing Floating Point Numbers¶
== can also be used to compare floating point numbers:
x = 10
y = (x ** (1 / 3)) ** 3
x == yFalseWhy False? Shouldn’t ?
Floating point numbers have finite precisions and so the computation may not be exact.
In practice, a small error in the calculation can be tolerated. Instead of checking equality of floating point numbers, we can check closeness up to a specified tolerance:
abs_tol = 1e-9
y - abs_tol <= x <= y + abs_tolTrueabs_tol, often denoted as , is a positive number called the absolute tolerance.
Why call it absolute tolerance?
Note that the test remains unchanged if we swap x and y:
abs_tol = 1e-9
x - abs_tol <= y <= x + abs_tolTrueUsing the absolute function abs, we can also rewrite the comparison as follows:
abs_tol = 1e-9
abs(x - y) <= abs_tolTrueIs an absolute tolerance of 1e-9 good enough?
The same absolute tolerance fails if we set x = 1e10 instead of 10.
x = 1e10
y = (x ** (1 / 3)) ** 3
abs_tol = 1e-9
abs(x - y) <= abs_tolFalseWhy absolute tolerance fails?
In floating-point representation, the radix point floats across different scales according to the exponent. The precision error in the mantissa is therefore relative to the scale of the number.
We should instead check whether is within a certain percentage of :
rel_tol = 1e-9
y * (1 - rel_tol) <= x <= y * (1 + rel_tol)TrueNote that the above test is not symmetric between and , i.e., is relatively close to does not necessarily mean is relatively close to .
x = 1
y = 2
rel_tol = 0.5
y * (1 - rel_tol) <= x <= y * (1 + rel_tol), x * (1 - rel_tol) <= y <= x * (1 + rel_tol)(True, False)To make the comparison symmetric, we can use the absolute difference as follows:
Why use but not ?
x = 1e10
y = (x ** (1 / 3)) ** 3rel_tol = 1e-9
### BEGIN SOLUTION
abs(x - y) <= rel_tol * max(abs(x), abs(y))
### END SOLUTIONTrueWhat if or is very close to 0? For instance, can you change rel_tol so that your solution above returns True for the following values of x and y?
x = 1e-15
y = 0When does relative tolerance fail?
If or is very close to 0, the relative tolerance test becomes ineffective, and so absolute tolerance is a more meaningful test. In particular, with , (4) becomes
In the nontrivial case , the test is independent of the value of , and therefore cannot check whether is close to 0. In particular, since normally, and the test would always fail unless exactly, which defeats the purpose of allowing a tolerance.
For a more flexible comparison, one can impose both the absolute and relative tolerances:
This is implemented by the function isclose from math module with the default
relative tolerance equal to
1e-9, andabsolute tolerance equal to
0.0.
math.isclose?Signature: math.isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)
Docstring:
Determine whether two floating-point numbers are close in value.
rel_tol
maximum difference for being considered "close", relative to the
magnitude of the input values
abs_tol
maximum difference for being considered "close", regardless of the
magnitude of the input values
Return True if a is close in value to b, and False otherwise.
For the values to be considered close, the difference between them
must be smaller than at least one of the tolerances.
-inf, inf and NaN behave similarly to the IEEE 754 Standard. That
is, NaN is not close to anything, even itself. inf and -inf are
only close to themselves.
Type: builtin_function_or_methoddef isclose(x, y, *, rel_tol=1e-09, abs_tol=0.0):
### BEGIN SOLUTION
return x==y or abs(x - y) <= max(rel_tol * max(abs(x), abs(y)), abs_tol)
### END SOLUTIONx = 10
y = (x ** (1 / 3)) ** 3
assert isclose(x, y)
x = 1e10
y = (x ** (1 / 3)) ** 3
assert isclose(x, y) and not isclose(x, y, rel_tol=0, abs_tol=1e-9)
assert not isclose(1e-15, 0)
assert isclose(float('inf'), float('inf'))
assert not isclose(float('nan'), float('nan'))%%hermes
Explain in one paragraph why comparing floating point numbers with == is
dangerous, and what pitfalls may arise in real-world engineering applications.↻ Session: main (ACP: ec63f1db-2c0…)
See Also
Comparing floating-point numbers with == is dangerous because floats are stored as binary approximations in IEEE 754 format, so many decimal values (like 0.1) cannot be represented exactly. Arithmetic operations accumulate tiny rounding errors, so 0.1 + 0.2 doesn’t equal 0.3 bit-for-bit — it’s 0.30000000000000004. In real-world engineering, this means a control system’s threshold check (if temp == 100.0) can silently fail, a financial calculation can report mismatched balances after repeated additions, and physics simulations can produce divergent results depending on the order of operations. The fix is to compare within a tolerance (abs(a - b) < epsilon), not with exact equality.
Conditional Constructs¶
To illustrate how Python can carry out conditional execution, we will consider writing a program that sorts values in ascending order.
If-Then Construct¶
How to sort two values?
Given two values are stored as x and y, we want to
print(x,y)ifx <= y, andprint(y,x)ify < x.
Such a program flow is often represented by a flowchart like the following:
sort_two_values_fc1How to read the flowchart?
A flowchart uses arrows to connects a set of annotated blocks. The rules were first specified by ANSI and later adopted in ISO 5807.
Why to use a program flowchart?
A program flowchart is a powerful way of describing an algorithm quickly. Unlike a text-based programming language:
The rules governing the program flow can be shown explicitly by arrows.
The annotated graphical blocks can convey the meaning faster using visual clues.
How to implement the flowchart in Python?
It is often useful to delay detailed implementations until we have written an overall skeleton. To leave a block empty, Python uses the keyword pass.
# write a code skeleton
def sort_two_values(x, y):
pass
# print the smaller value first followed by the larger one
sort_two_values(1, 0)
sort_two_values(1, 2)Python provides the if statement to implement the control flow specified by the diamond boxes in the flowchart.
def sort_two_values(x, y):
if x <= y:
pass
# print x before y
if y < x: pass # print y before x
sort_two_values(1, 0)
sort_two_values(1, 2)To complete the implementations specified by the parallelogram boxes in the flow chart, we fill in the bodies/suites of the if statements as follows:
def sort_two_values(x, y):
if x <= y:
print(x, y)
if y < x: print(y, x)
@interact(x="1", y="0")
def sort_two_values_app(x, y):
print("Values in ascending order:")
sort_two_values(eval(x), eval(y))Test the program by filling in different values of x and y above.
How to indent code in Python?
As Python uses indentation to indicate code blocks or suites:
print(x, y)(Line 3) is indented to the right ofif x <= y:(Line 2) to indicate it is the body of the if statement.For convenience,
if y < x: print(y, x)(Line 4) is a one-liner for anifstatement that only has one line in its block.Both
ifstatements (Line 2-4) are indented to the right ofdef sort_two_values(x,y):(Line 1) to indicate that they are part of the body of the functionsort_two_values.
The style guide recommends using 4 spaces for each indentation. In JupyterLab, you can simply type the tab key.
We can visualize the execution as follows. Step through the execution to
see which lines are skipped, and
understand why they are skipped.
%%pytutor
def sort_two_values(x, y):
if x <= y:
print(x, y)
if y < x: print(y, x)
sort_two_values(1, 0)
sort_two_values(1, 2)If-Then-Else Construct¶
Can the sorting algorithm be improved further?
Hint
not (x <= y) and (y < x) is a tautology, i.e., always true.
Consider the following modified flowchart:
sort_two_values_fc2This flowchart can be implemented by the else clause of the if statement as follows:
%%pytutor
def sort_two_values(x, y):
if x <= y:
print(x, y)
else:
print(y, x)
sort_two_values(1, 0)
sort_two_values(1, 2)Can we shorten the code to one line? This is possible with the conditional expression.
def sort_two_values(x, y):
print(("{0} {1}" if x <= y else "{1} {0}").format(x, y))
@interact(x="1", y="0")
def sort_two_values_app(x, y):
print("Values in ascending order:")
sort_two_values(eval(x), eval(y))Solution to Exercise 5
A conditional expression must be an expression:
It must give a value under all cases. To enforce that,
elsekeyword must be provided.An assignment statement does not return any value and therefore cannot be used for the conditional expression.
x = 1 if True else 0is valid becausex =is not part of the conditional expression.
Nested Conditionals¶
Now, consider a slightly more challenging problem of sorting three values instead of two. A feasible algorithm is as follows:
sort_three_values_fcTo implement flowchart, we can use nested conditional constructs, where one conditional statement is placed within another, allowing for multiple layers of condition checks:
def sort_three_values(x, y, z):
if x <= y <= z:
print(x, y, z)
else:
if x <= z <= y:
print(x, z, y)
else:
if y <= x <= z:
print(y, x, z)
else:
if y <= z <= x:
print(y, z, x)
else:
if z <= x <= y:
print(z, x, y)
else:
print(z, y, x)
def test_sort_three_values():
sort_three_values(0, 1, 2)
sort_three_values(0, 2, 1)
sort_three_values(1, 0, 2)
sort_three_values(1, 2, 0)
sort_three_values(2, 0, 1)
sort_three_values(2, 1, 0)
test_sort_three_values()0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
Imagine what would happen if we have to sort many values. The program will not only be long, but also fat due to the indentation. To avoid an excessively long line due to the indentation, Python provides the elif keyword that combines else and if.
def sort_three_values(x, y, z):
if x <= y <= z:
print(x, y, z)
elif x <= z <= y:
print(x, z, y)
elif y <= x <= z:
print(y, x, z)
elif y <= z <= x:
print(y, z, x)
elif z <= x <= y:
print(z, x, y)
else:
print(z, y, x)
test_sort_three_values()0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
def sort_three_values(x, y, z):
### BEGIN SOLUTION
# How many comparisons are needed in a program to sort n numbers?
if x > y:
x, y = y, x
if y > z:
y, z = z, y
if x > y:
x, y = y, x
print(x, y, z)
### END SOLUTION
sort_three_values(10, 17, 14)10 14 17
Solution to Exercise 7 #
Consider
%%hermes
Write a function sort_four_values(x, y, z, w) that prints four values in
ascending order using only if-else statements, no sorted() or .sort().A generated program was:
def sort_four_values(x, y, z, w):
# Put x ≤ y
if x > y:
x, y = y, x
# Put z ≤ w
if z > w:
z, w = w, z
# Put smallest of both pairs in x, largest in y
if x > z:
x, z = z, x
# Put largest of both pairs in w, second largest in y
if y > w:
y, w = w, y
# Now x is smallest, w is largest; fix y and z
if y > z:
y, z = z, y
print(x, y, z, w)To generate the test:
%%hermes
Use `itertools.permutations([1, 2, 3, 4])` to
systematically verify.This gives
import itertools
for p in itertools.permutations([1, 2, 3, 4]):
sort_four_values(*p)A correct sort_four_values should print 1 2 3 4 for
all input permutations.
Sorting algorithms
A sorting algorithm refers to the procedure of sorting any number of values
in order. The performance of a sorting algorithm is measured primarily by its
time complexity (the number of
comparisons and swaps it makes) and its space
complexity
(the amount of additional memory it requires). It would be even better if the
program is short and easy to read, but that depends partly on the design of the
programming language. Python implemented its default sorted function using
the Timsort
algorithm.
See if you can understand how it works and how well it works.
%%hermes
Explain in one paragraph what the minimum possible number of comparisons needed
to sort n numbers is and why it is not the same as the maximum number of
inversions.↻ Session: main (ACP: ec63f1db-2c0…)
See Also
The minimum number of comparisons needed to sort n numbers is Ω(n log n) in the worst case, because comparison-based sorting can be modeled as a decision tree with n! leaf nodes (one per possible permutation), and a binary tree with L leaves has height at least ⌈log₂ L⌉. This is fundamentally different from the maximum number of inversions, which is n(n−1)/2 — that counts how many pairs are out of order in the worst case (a reversed list), not how many questions a smart algorithm must ask. One is a lower bound on information needed (log of permutations), the other is an upper bound on pairwise disorder — they measure completely different things, which is why insertion sort can take O(n²) swaps in the worst case while mergesort always runs in O(n log n).
Boolean Operations¶
Since chained comparisons are non-associative, it follows a different evaluation rule than arithmetic operators.
E.g., 1 <= 2 < 3 != 4 is equivalent to:
1 <= 2 and 2 < 3 and 3 != 4TrueThe above is called a compound boolean expression, which is formed using the
boolean/logical operator and.
Why use boolean operators?
What if we want to check whether a number is either or ?
# Check if a number is outside a range.
@interact(x="15")
def check_out_of_range(x):
x_ = float(x)
is_out_of_range = x_ < 0 or x_ >= 100
print("Out of range [0,100):", is_out_of_range)%%hermes
Explain in a paragraph why the set of operators including only "and" is not
functionally complete.↻ Session: main (ACP: ec63f1db-2c0…)
See Also
The set {AND} is not functionally complete because AND is truth-preserving: whenever all inputs are True, the output is always True, no matter how you combine them. This means you can never produce a NOT operation — if you feed only True values into any expression built from AND alone, you’ll never get False. Since there are 2^(2ⁿ) possible Boolean functions on n variables (including functions like NOT, NOR, and NAND that do output False when all inputs are True), and AND can only express a tiny subset of them, it alone cannot represent every possible Boolean function.
%%hermes
Explain in a paragraph why the set of operators "and", "or", and "not" is
functionally complete.↻ Session: main (ACP: ec63f1db-2c0…)
See Also
The set {AND, OR, NOT} is functionally complete because any Boolean function can be expressed as a disjunctive normal form (DNF) — a combination of ANDs, ORs, and NOTs. Specifically, every truth table row where the output is True can be captured by one AND-term (the conjunction of all inputs, each either as-is or negated), and OR-ing those terms together reconstructs the entire function. Since this construction works for any truth table, {AND, OR, NOT} can express all 2^(2ⁿ) possible Boolean functions on n variables.
The following table is called a truth table. It enumerates all possible input and output combinations for each boolean operator available in Python:
Table 2:Truth table for different logical operators
x | y | x and y | x or y | not x |
|---|---|---|---|---|
True | True | True | True | False |
True | False | False | True | False |
False | True | False | True | True |
False | False | False | False | True |
Solution to Exercise 8
Expression A evaluates to
Truebecauseandhas higher precedence and so the expression has the same value asTrue or (False and True).Expression B evaluates to
Falsebecauseandis left associative and so the expression has the same value as(True and False) and True.Expression C evaluates to
Truebecauseandhas a higher precedence and so the expression has the same value asTrue or (True and False). Note that(True or True) and Falseevaluates to somethingFalseinstead, so precedence matters.
A compound boolean expression actually uses a short-circuit evaluation.
To understand this, we will use the following function to evaluate a boolean expression verbosely.
def verbose(id, expr):
"""Identify evaluated boolean expressions."""
print(id, "evaluated:", expr)
return exprFor instance:
verbose("1st expression", True)
verbose("2nd expression", False)1st expression evaluated: True
2nd expression evaluated: False
FalseShort-circuit or¶
Consider evaluating True or False and True in a verbose manner:
verbose("A", verbose(1, True) or verbose(2, False) and verbose(3, True))1 evaluated: True
A evaluated: True
TrueWhy the second and third expressions are not evaluated?
Because True or ... must be True (why?) so Python does not look further. From the documentation:
Short-circuit evaluation of or
The expression x or y
first evaluates
x;if
xis true, its value is returned;otherwise,
yis evaluated and the resulting value is returned.
Put it another way, (x or y) translates to the following
_ if (_ := x) else yProgram 1:Translation of (x or y)
except for the extra variable _. For example, True or False and True translates to _ if (_ := True) else False and True:
verbose("A", (_ if (_ := verbose(1, True)) else verbose(2, False) and verbose(3, True)))1 evaluated: True
A evaluated: True
TrueShort-circuit and¶
Now, consider evaluating False or False and True in a verbose manner:
False or False and TrueFalseverbose("B", verbose(4, False) or verbose(5, False) and verbose(6, True))4 evaluated: False
5 evaluated: False
B evaluated: False
FalseWhy expression 6 is not evaluated?
False or False and ... must be False so Python does not look further.
Short-circuit evaluation of and
The expression x and y first evaluates x;
if x is false, its value is returned;
otherwise, y is evaluated and the resulting value is returned.
Put it another way, (x and y) translate to the following
_ if not (_ := x) else yProgram 2:Translation of (x and y)
except for the extra variable _. For example, False or False and True translates to False or (_ if not (_ := False) else True):
verbose(
"B", verbose(4, False) or (_ if not (_ := verbose(5, False)) else verbose(6, True))
)4 evaluated: False
5 evaluated: False
B evaluated: False
FalseWho introduced short-circuit evaluation in programming?
Short-circuit evaluation was introduced by John McCarthy (computer scientist), one of the researchers who coined the term artificial intelligence.
Non-Boolean Values for Boolean Operations and Control Flow Statements¶
Interestingly, Python also allows logical operations to have non-boolean operands and return values. From the documentation:
Boolean interpretation
In the context of Boolean operations, and also when expressions are used by control flow statements, the following values are interpreted as false:
FalseNoneNumeric zero of all types
Empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets)
All other values are interpreted as true.
The following is an application of this rule.
print("You have entered", input() or "nothing") 1
You have entered 1
Solution to Exercise 9 #
The code replaces an empty user input by the default string
nothingbecause an empty string is regarded as False in a boolean operation.If user input is non-empty, it is regarded as True in the boolean expression and returned immediately as the value of the boolean operation.
input(1) or input(2) and input(3)1
2
3
''Solution to Exercise 10
Enter ’ ', ‘’ and then ‘’.
### BEGIN SOLUTION
_ if (_ := input(1)) else (_ := input(3)) if (_ := input(2)) else _
### END SOLUTIONIs an empty string "" equal to False?
"" equal to False?Try "" == False.
An empty string is regarded as false in a boolean operation but
a comparison operation is not a boolean operation, even though it forms a boolean expression.
Should the following return False?
0 == False and 1 == TrueTry and explain what you see.
%%hermes
In Python, explain in a paragraph what the following expression evaluates to:
0 == False and 1 == True↻ Session: main (ACP: ec63f1db-2c0…)
See Also
In Python, True and False are subclasses of int where True == 1 and False == 0, so 0 == False is True and 1 == True is also True, making the whole expression evaluate to True. This is a quirk of Python’s type system — booleans are integers under the hood, so numeric equality with == crosses the type boundary, which can lead to surprising results like 0 == False being True even though they’re conceptually different.
%%hermes
Give a real-world example where short-circuit evaluation is used for safety
checks before performing expensive or dangerous operations.↻ Session: main (ACP: ec63f1db-2c0…)
See Also
A classic example is checking if a pointer/reference exists before accessing it:
if user is not None and user.has_admin_privileges():
delete_database()Here, user is not None is evaluated first. If user is None, the second part (user.has_admin_privileges()) is never executed — preventing a NoneType error that would crash the program (or worse, skip the authorization check entirely). Without short-circuit evaluation, the code would try to call a method on None, which would either crash or bypass the safety guard, potentially allowing unauthorized access to delete_database().
The equality operator
==consists of two equal signs, different from the assignment operator=.Different from C or JavaScript:
We can write
1 != 2asnot 1 == 2but not!(1 == 2)because!is not a logical operator. It is used to call a system shell command in IPython.
Thonny is an IDE based on TKinter, which requires a proper desktop environment to run.
You can also launch a terminal in the remote desktop and run the command
thonny.You can copy and paste code into the remote desktop by clicking
Remote Clipboardbutton at the top right of the browser page and pasting your code into the textbox that appears below.Short-cut keys for Thonny may not work as the keys may be captured by the browser or your OS before they can reach Thonny.