Last time we introduced Boolean types, specifically the
bool type in Python.
s = "hello" >>> s.startswith('h') True >>> s.startswith('b') False
bools have only two values:
False. As their name suggests they
are used to express what is true and what isn’t.
All Boolean operations can be implemented with a combination of the basic
boolean operations AND, OR and NOT. Or in Python
first two are binary operations, i.e. have two inputs, the last is unary, i.e. has a
>>> not True False >>> not False True >>> True and False False >>> True or False True
Boolean operators are often expressed as truth tables, e.g.
a <operator> b:
Much like arithmetic operators, boolean operators have precedence:
PI Questions (Boolean operators)1
We earlier saw some string methods that return
bool. Another common way to
bools is with relational operators applied to numerical (and other)
<, >, <=, >=, ==, !=
Recall that in Python (unlike math),
= is assignment, and
== is equality
!= implementing “not equals”).
>>> 1 < 2 True >>> 2 > 1 True >>> 2 != 1 True >>> 2 == 2 True >>> 2 == 2.0 True >>> x = 2 >>> 1 < x < 3 True >>> s == "hello" True >>> s == "h" False >>> s == "h" True >>> s[:2] == "he" True
PI Questions (relational operators)1
One of the key uses for
bools is making decisions. So far none of our
programs have made any choices. Being able to do so is very powerful.
The general pattern for conditional statements in Python:
if (boolean expression A): statement1 statement2 elif (boolean expression B): statement3 statement4 else: statement5 statement6 statement7 statement8
If A evaluates to
True which statements will be executed? Statements 1,2,7,8.
What if A evaluates to
False? If B evaluates to
True, statements 3,4,7,8.
If neither A or B evaluate to
True, then statements 5,6,7,8 will execute.
Only one of the
else is “selected” and its body executed,
even if multiple of the boolean expressions would have evaluated to true, and
that selection occurs in order.
else are optional and no branch of the conditional needs to
be selected. Multiple
elifs are permitted.
Some other examples:
def is_odd(n): if n % 2 == 1: return True else: return False def positivity(n): if n == 0: print(n, "is zero") elif n > 0: print(n, "is positive") else: print(n, "is negative")
PI Questions (conditional statements)1
A note about coding “best practices”. Can we write
is_odd more concisely?
Yes. Almost anytime we are returning a boolean from a conditional statement we can
do so more concisely, e.g.
def is_odd(n): return (n % 2) == 1
Why is this better style? Recall that good coding style is often about minimizing the cognitive burden of reading code. When returning boolean values from within in if-else statement we first need understand the condition expression and then map it to separate, possibly different return values. That is additional cognitive burden compared to just needing to understand the condition expression.
We can calculate π via simulation. Consider a quarter circle inscribed inside a square of side 1 (i.e. with an area of 1). If we randomly select points inside the square, approximately a π/4 fraction of those points should be inside the quarter circle. By calculating the ratio of randomly sampled points inside the circle to the total number of sample, we can estimate the value of π. This approach is called Monte Carlo sampling.
Let’s implement a function
calculate_pi with a single parameter, the number of sampled points. What should we expect as we increase the number of samples? We will get closer and closer (probabilistically) to the actual value of pi. For example:
>>> calculate_pi(10) 3.2 >>> calculate_pi(100) 3.08 >>> calculate_pi(1000) 3.088 >>> calculate_pi(10000) 3.1604 >>> calculate_pi(100000) 3.14324 >>> calculate_pi(1000000) 3.141572 >>> calculate_pi(10000000) 3.1411364
Show a potential implementation…
import math, random def calculate_pi(num_samples): """ Approximate pi via Monte Carlo sampling Args: num_samples: number of Monte Carlo samples Returns: Approximate value of pi """ in_circle = 0 for i in range(num_samples): # Generate random "dart" inside unit square x = random.uniform(0, 1) y = random.uniform(0, 1) # Determine distance to origin dist = math.sqrt(x*x + y*y) # Count number of darts inside the circle if dist <= 1: in_circle += 1 # equivalent to in_circle = in_circle + 1 # Calculate pi based on ratio of "darts" inside circle vs total samples return (4 * in_circle) / num_samples