Control Flow in Python.
Kuo, Yao-Jen yaojenkuo@ntu.edu.tw from DATAINPOINT
In computer science, control flow (or flow of control) is the order in which individual statements, instructions or function calls of an imperative program are executed or evaluated. The emphasis on explicit control flow distinguishes an imperative programming language from a declarative programming language.
while
loopfor
loopIn computer science, conditional statements are features of a programming language, which perform different computations or actions depending on whether a programmer-specified boolean condition evaluates to true or false.
Source: https://en.wikipedia.org/wiki/Conditional_(computer_programming)
bool
.if CONDITION:
# statements to be executed if CONDITION is evaluated as True.
{ }
to define a code block¶bool
for condition¶==
, !=
, >
, <
, >=
, <=
, in
, not in
and
, or
, not
if
for conditional executions¶if CONDITION:
# statements to be executed if CONDITION is evaluated as True.
We can use pythontutor.com to explore the execution of our code.
def return_msg_if_positive(x: int) -> str:
if x > 0:
return "{} is positive.".format(x)
print(return_msg_if_positive(5566))
print(return_msg_if_positive(-5566))
5566 is positive. None
if
and else
to perform alternative executions¶Since the condition must be true or false, exactly one of the alternatives will run.
if CONDITION:
# statements to be executed if CONDITION is evaluated as True.
else:
# statements to be executed if CONDITION is evaluated as False.
def return_msg_whether_positive_or_not(x: int) -> str:
if x > 0:
msg = "{} is positive.".format(x)
else:
msg = "{} is not positive.".format(x)
return msg
print(return_msg_whether_positive_or_not(5566))
print(return_msg_whether_positive_or_not(-5566))
5566 is positive. -5566 is not positive.
if
, elif
, and else
to perform chained conditionals¶The else
clause makes our conditionals collectively exhaustive.
if CONDITION_A:
# statements to be executed if CONDITION_A is evaluated as True.
elif CONDITION_B:
# statements to be executed if CONDITION_A is evaluated as False and CONDITION_B is evaluated as True.
elif CONDITION_C:
# statements to be executed if CONDITION_A and CONDITION_B are both evaluated as False and CONDITION_C is evaluated as True.
else:
# statements to be executed if CONDITION_A, CONDITION_B, and CONDITION_C are all evaluated as False.
def return_msg_whether_positive_negative_or_neutral(x: int) -> str:
if x > 0:
msg = "{} is positive.".format(x)
elif x < 0:
msg = "{} is negative.".format(x)
else:
msg = "{} is neutral.".format(x)
return msg
print(return_msg_whether_positive_negative_or_neutral(5566))
print(return_msg_whether_positive_negative_or_neutral(-5566))
print(return_msg_whether_positive_negative_or_neutral(0))
5566 is positive. -5566 is negative. 0 is neutral.
def return_msg_whether_positive_negative_or_neutral(x: int) -> str:
if x > 0:
msg = "{} is positive.".format(x)
if x < 0:
msg = "{} is negative.".format(x)
if x == 0:
msg = "{} is neutral.".format(x)
return msg
print(return_msg_whether_positive_negative_or_neutral(5566))
print(return_msg_whether_positive_negative_or_neutral(-5566))
print(return_msg_whether_positive_negative_or_neutral(0))
5566 is positive. -5566 is negative. 0 is neutral.
def return_msg_whether_positive_negative_or_neutral(x: int) -> str:
if x > 0:
msg = "{} is positive.".format(x)
else:
if x < 0:
msg = "{} is negative.".format(x)
else:
msg = "{} is neutral.".format(x)
return msg
print(return_msg_whether_positive_negative_or_neutral(5566))
print(return_msg_whether_positive_negative_or_neutral(-5566))
print(return_msg_whether_positive_negative_or_neutral(0))
5566 is positive. -5566 is negative. 0 is neutral.
FizzBuzz
for example¶Fizz buzz (often spelled FizzBuzz in this context) has been used as an interview screening device for computer programmers. Writing a program to output the first 100 FizzBuzz numbers is a trivial problem for any would-be computer programmer, so interviewers can easily filter out those with insufficient programming ability.
# chained conditionals
def fizz_buzz(x: int) -> str:
if x % 15 == 0:
ans = "Fizz Buzz"
elif x % 3 == 0:
ans = "Fizz"
elif x % 5 == 0:
ans = "Buzz"
else:
ans = x
return ans
fizz_buzz(15)
'Fizz Buzz'
# non-chained conditionals
def fizz_buzz(x: int) -> str:
if x % 3 == 0:
ans = "Fizz"
if x % 5 == 0:
ans = "Buzz"
if x % 15 == 0:
ans = "Fizz Buzz"
if (x % 15 != 0) and (x % 3 != 0) and (x % 5 != 0):
ans = x
return ans
fizz_buzz(15)
'Fizz Buzz'
Like the slicing syntax of lists:
while
loopfor
loopwhile
loop is used to repeat one or more code statements as long as the condition is evaluated as True
¶i = 0 # start
while CONDITION: # stop
# repeated statements
i += 1 # step
i += 1
as in i = i + 1
i -= 1
as in i = i - 1
i *= 1
as in i = i * 1
i /= 1
as in i = i / 1
for
loop is used to step an element through an iterable until the end is reached¶for i in ITERABLE: # start/stop/step
# repeated statements
We can use pythontutor.com to explore the execution of our code.
i = 1 # start
while i < 11: # stop
print(i)
i += 2 # step
1 3 5 7 9
for i in range(1, 10, 2): # start/stop/step => help(range)
print(i)
1 3 5 7 9
range()
function to create a sequence¶help(range)
Help on class range in module builtins: class range(object) | range(stop) -> range object | range(start, stop[, step]) -> range object | | Return an object that produces a sequence of integers from start (inclusive) | to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1. | start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3. | These are exactly the valid indices for a list of 4 elements. | When step is given, it specifies the increment (or decrement). | | Methods defined here: | | __bool__(self, /) | self != 0 | | __contains__(self, key, /) | Return key in self. | | __eq__(self, value, /) | Return self==value. | | __ge__(self, value, /) | Return self>=value. | | __getattribute__(self, name, /) | Return getattr(self, name). | | __getitem__(self, key, /) | Return self[key]. | | __gt__(self, value, /) | Return self>value. | | __hash__(self, /) | Return hash(self). | | __iter__(self, /) | Implement iter(self). | | __le__(self, value, /) | Return self<=value. | | __len__(self, /) | Return len(self). | | __lt__(self, value, /) | Return self<value. | | __ne__(self, value, /) | Return self!=value. | | __reduce__(...) | Helper for pickle. | | __repr__(self, /) | Return repr(self). | | __reversed__(...) | Return a reverse iterator. | | count(...) | rangeobject.count(value) -> integer -- return number of occurrences of value | | index(...) | rangeobject.index(value) -> integer -- return index of value. | Raise ValueError if the value is not present. | | ---------------------------------------------------------------------- | Static methods defined here: | | __new__(*args, **kwargs) from builtins.type | Create and return a new object. See help(type) for accurate signature. | | ---------------------------------------------------------------------- | Data descriptors defined here: | | start | | step | | stop
primes = [2, 3, 5, 7, 11]
i = 0 # start
while i < len(primes): # stop
print(primes[i])
i += 1 # step
2 3 5 7 11
for i in primes: # start/stop/step
print(i)
2 3 5 7 11
while
versus for
when dealing with iterations?¶for
to iterate over lists, dictionaries, and other iterableswhile
if our operations involve randomness or uncertaintyfrom random import randint
def binary_search(low: int=1, high: int=100) -> list:
guess = (low + high) // 2
rand_int = randint(low, high)
guess_history = [guess]
while guess != rand_int:
if guess > rand_int:
high = guess
guess = (low + high) // 2
elif guess < rand_int:
low = guess
guess = (low + high) // 2 + 1
guess_history.append(guess)
return rand_int, guess_history
print(binary_search())
print(binary_search())
print(binary_search())
(85, [50, 76, 89, 82, 86, 84, 86, 85]) (14, [50, 25, 13, 20, 16, 14]) (92, [50, 76, 89, 95, 92])
def int_to_bin_str(x: int) -> str:
if x < 2:
return str(x)
binary_str = ""
while x > 0:
modulo = x % 2
binary_str = str(modulo) + binary_str
x //= 2
return binary_str
print(int_to_bin_str(2)) # bin(2)
print(int_to_bin_str(3)) # bin(3)
print(int_to_bin_str(4)) # bin(4)
10 11 100
An iterable is any Python object capable of returning its elements one at a time, permitting it to be iterated over in a for
loop. Familiar examples of iterables include lists, tuples, and strings.
help(iter)
Help on built-in function iter in module builtins: iter(...) iter(iterable) -> iterator iter(callable, sentinel) -> iterator Get an iterator from an object. In the first form, the argument must supply its own iterator, or be a sequence. In the second form, the callable is called until it returns the sentinel.
help(next)
Help on built-in function next in module builtins: next(...) next(iterator[, default]) Return the next item from the iterator. If default is given and the iterator is exhausted, it is returned instead of raising StopIteration.
may4th = "Luke, use the Force!"
I = iter(may4th)
print(next(I))
print(next(I))
print(next(I))
print(next(I))
L u k e
int
, float
, or bool
?¶def is_x_iterable(x) -> str:
try:
I = iter(x)
return True
except TypeError as e:
return e
print(is_x_iterable(5566))
print(is_x_iterable(5566.0))
print(is_x_iterable(False))
print(is_x_iterable(True))
'int' object is not iterable 'float' object is not iterable 'bool' object is not iterable 'bool' object is not iterable
primes = [2, 3, 5, 7, 11]
for i in primes:
print(i)
2 3 5 7 11
Use .keys()
, .values()
, and .items()
to help us iterate over a dictionary.
the_celtics = {
'isNBAFranchise': True,
'city': "Boston",
'fullName': "Boston Celtics",
'tricode': "BOS",
'teamId': 1610612738,
'nickname': "Celtics",
'confName': "East",
'divName': "Atlantic"
}
print(the_celtics.keys())
print(the_celtics.values())
print(the_celtics.items())
dict_keys(['isNBAFranchise', 'city', 'fullName', 'tricode', 'teamId', 'nickname', 'confName', 'divName']) dict_values([True, 'Boston', 'Boston Celtics', 'BOS', 1610612738, 'Celtics', 'East', 'Atlantic']) dict_items([('isNBAFranchise', True), ('city', 'Boston'), ('fullName', 'Boston Celtics'), ('tricode', 'BOS'), ('teamId', 1610612738), ('nickname', 'Celtics'), ('confName', 'East'), ('divName', 'Atlantic')])
for k in the_celtics.keys():
print(k)
isNBAFranchise city fullName tricode teamId nickname confName divName
for v in the_celtics.values():
print(v)
True Boston Boston Celtics BOS 1610612738 Celtics East Atlantic
for k, v in the_celtics.items():
print("{}: {}".format(k, v))
isNBAFranchise: True city: Boston fullName: Boston Celtics tricode: BOS teamId: 1610612738 nickname: Celtics confName: East divName: Atlantic
print()
list
.str
.list
¶def collecting_two_digit_integers(x: list) -> list:
out = []
for elem in x:
if 10 <= elem <= 99:
out.append(elem)
return out
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 101, 103]
collecting_two_digit_integers(primes)
[11, 13, 17, 19, 23, 29]
str
¶def collecting_non_vowels(x: str) -> str:
out = ""
for char in x:
char_lower = char.lower()
if char_lower not in {'a', 'e', 'i', 'o', 'u'}:
out += char # out = out + char
return out
print(collecting_non_vowels("Python"))
print(collecting_non_vowels("Anaconda"))
Pythn ncnd
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 101, 103]
summation = 0
counts = 0
for i in primes:
summation += i # summation = summation + 1
counts += 1 # counts = counts + 1
print(summation)
print(counts)
333 12
Built-in functions sum()
and len()
work like a charm.
print(sum(primes))
print(len(primes))
333 12
batman_trilogy = {
"titles": ["Batman Begins", "The Dark Knight", "The Dark Knight Rises"],
"release_dates": ["June 15, 2005", "July 18, 2008", "July 20, 2012"],
"director": ["Christopher Nolan", "Christopher Nolan", "Christopher Nolan"]
}
for v in batman_trilogy.values():
for i in v:
print(i)
Batman Begins The Dark Knight The Dark Knight Rises June 15, 2005 July 18, 2008 July 20, 2012 Christopher Nolan Christopher Nolan Christopher Nolan
break
to early break an iterationcontinue
to skip certain stepsdef early_break_if_summation_exceeds_fifty(x: range) -> None:
summation = 0
for i in x:
summation += i
print(i)
if summation >= 50:
break
early_break_if_summation_exceeds_fifty(range(100))
0 1 2 3 4 5 6 7 8 9 10
def print_only_evens(x: range) -> None:
summation = 0
for i in x:
if i % 2 == 1:
continue
print(i)
print_only_evens(range(10))
0 2 4 6 8
Source: Google Search
Python mistakes come in three basic flavors:
Errors where the code is not valid Python (generally easy to fix).
# Python does not need braces to create a code block
for (i in range(10)) {
print(i)
}
Errors where syntactically valid code fails to execute, perhaps due to invalid user input (sometimes easy to fix)
NameError
TypeError
ZeroDivisionError
IndexError
# IndexError
my_favorite_boy_group = "5566"
print(my_favorite_boy_group[4])
Errors in logic: code executes without a problem, but the result is not what you expect (often very difficult to identify and fix)
def product(x: list) -> int:
"""
x: an iterable.
Return the product values of x.
"""
prod = 0 # set
for i in x:
prod *= i
return prod
print(product([5, 5, 6, 6])) # expecting 900
0
try
and except
to catch exceptions¶try:
# sequence of statements if everything is fine
except TYPE_OF_ERROR:
# sequence of statements if something goes wrong
try:
my_favorite_boy_group = "5566"
print(my_favorite_boy_group[4])
except IndexError:
print("Encountering a IndexError.")
Encountering a IndexError.
# Print out error message directly
try:
my_favorite_boy_group = "5566"
print(my_favorite_boy_group[4])
except IndexError as e:
print(e)
string index out of range
try:
print(5566 / 0)
except ZeroDivisionError as e:
print(e)
division by zero
# It is optional to specify the type of error
try:
print(5566 / 0)
except:
print("Encountering a whatever error.")
Encountering a whatever error.