More Control Flow Tools
Published:
This lesson covers An Informal Introduction to Python 3.10.5, https://docs.python.org/3/tutorial/introduction.html
Control Statements
if
Statements
x = int(input("Please enter an integer: "))
if x < 0:
x = 0
print('Negative changed to zero')
elif x == 0: # elif avoid excessive indention
print('Zero')
elif x == 1:
print('Single')
else:
print('More')
# without elif
x = int(input("Please enter an integer: "))
if x < 0:
x = 0
print('Negative changed to zero')
else:
if x == 0:
print('Zero')
else:
if x == 1:
print('Single')
else:
print('More')
for
Statements
words = ['cat', 'window', 'pythonisgood']
for w in words:
print(w, len(w))
# Create a sample collection
users = {'Hans': 'active',
'Éléonore': 'inactive',
'景太郎': 'active'}
"""
#Error since del change size during iteration
for user, status in users.items():
if status == 'inactive':
del users[user]
print(users)
"""
# Strategy: Iterate over a copy
for user, status in users.copy().items():
if status == 'inactive':
del users[user]
print(users)
# Strategy: Create a new collection
active_users = {}
for user, status in users.items():
if status == 'active':
active_users[user] = status
print(active_users)
The range(
Function
for i in range(5):
print(i)
print(list(range(5, 10))) # [5, 6, 7, 8, 9]
print(list(range(0, 9, 3))) # [0, 3, 6]
print(list(range(0, 11, 3))) # [0, 3, 6, 9]
print(list(range(-10, -100, -30))) # [-10, -40, -70]
# iterate over the indices of a sequence
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
print(i, a[i])
for i, v in enumerate(a):
print(i, v)
print(list(enumerate(a)))
questions = ['name', 'favorite color']
answers = ['A', 'blue']
for q, a in zip(questions, answers):
print(f"What is your {q}? It is {a}.")
for i in reversed(range(1, 10, 2)):
print(i)
basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
for i in sorted(basket):
print(i)
basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
for f in sorted(set(basket)):
print(f)
print(range(10)) # range(0, 10) # Iterable object
print(list(range(10))) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(sum(range(10))) # 45
break
and continue
Statements, and else
Clauses on Loops
# Loop statements may have an else clause;
# it is executed when the loop terminates through exhaustion
# of the iterable (with for) or when the condition becomes false (with while),
# but not when the loop is terminated by a break statement.
for i in range(1, 4):
print(i)
else: # Executed because no break in for
print("No Break")
for i in range(1, 4):
print(i)
break
else: # Not executed as there is a break
print("No Break")
"""
for item in container:
if search_something(item):
# Found it!
process(item)
break
else:
# Didn't find anything..
not_found_in_container()
"""
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, 'equals', x, '*', n//x)
break
else:
# loop fell through without finding a factor
print(n, 'is a prime number')
for num in range(2, 10):
if num % 2 == 0:
print("Found an even number", num)
continue
print("Found an odd number", num)
pass
Statements
while True:
pass # Busy-wait for keyboard interrupt (Ctrl+C)
class MyEmptyClass:
pass
def initlog(*args):
pass # Remember to implement this!
match
Statements
Defining Functions
def fib(n): # write Fibonacci series up to n
"""Print a Fibonacci series up to n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
return "" # else print will display None # without print displaying in cell is ok
print(fib) # <function fib at 0x110fb35e0>
print(fib(1000)) # 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
f = fib
print(f(100)) # 0 1 1 2 3 5 8 13 21 34 55 89
def fib2(n): # return Fibonacci series up to n
"""Return a list containing the Fibonacci series up to n."""
result = []
a, b = 0, 1
while a < n:
result.append(a) # see below
a, b = b, a+b
return result
f100 = fib2(100) # call it
print(f100) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
More on Defining Functions
Default Argument Values
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
# display prompt and wait for input
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1 # if neither y nor n
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
# Call with prompt only
ask_ok('Do you really want to quit?')
ask_ok('OK to overwrite the file?', 2)
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
i = 5
def f(arg=i):
print(arg)
i = 6
# default values are evaluated
# at the point of function definition
# in the defining scope
f() # 5
# difference
# when default is mutable object (can be changed)
# such as a list, dictionary, or instances of most classes
def f(a, L=[]):
L.append(a)
return L
print(f(1)) # [1] # Since Mutable same object updated
print(f(2)) # [1, 2]
print(f(3)) # [1, 2, 3]
# difference
# when default is mutable object (can be changed)
# such as a list, dictionary, or instances of most classes
# No sharing in subsequent function calls
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
print(f(1)) # [1] # default is None
print(f(2)) # [2]
print(f(3)) # [3]
Keyword Arguments
- Functions can be called with keyword arguments
kwarg=value
, - keyword arguments must follow positional arguments
- When a final formal parameter of the form
**name
is present, it receives a dictionary containing all keyword arguments except for those corresponding to a formal parameter
def network(inp, *layers): # layers is tuple
print(f"Input {inp}")
for i, layer in enumerate(layers):
print(f"Layer {i+1}: {layer}")
network(100, 80, 50, 30, 20, 1)
def network(inp, **layers): # layers is dict
print(f"Input {inp}")
for k, v in layers.items():
print(f"{k}: {v}")
network(100, layer1=80, layer3=50, layer5=30, layer7=20, out=1)
Special parameters
# / and * are optional if used these indicate type of parameters to be passed
# If / and * are not present then can be passes by position or keyword
# Parameteres before / - positional only no keyword
# Parameters after / - can be positional or keyword
# Parameters after * - keyword only
def network(inp1, inp2, /, inp3, *, inp4):
print(f"Input {inp1} {inp2} {inp3} {inp4}")
#network(inp1=10) # / indicate inp1 & inp2 are position-only no variable names
#network(inp1=10, inp2=12) # / indicate inp1 & inp2 are position-only no variable names
# network(10, 12) # inp3 required as position or variable
# error due to inp4 that is keywork only
# network(10, 12, 14)
# network(10, 12, inp3=14)
network(10, 12, inp3=14, inp4=16) # will work
network(10, 12, 14, inp4=16) # will work
#network(10, 12, 14, 16) # Inp4 must be keyword
Arbitrary Argument Lists
def concat(*args, sep):
return sep.join(args)
# any variable after variadic arguments must be keyword only\
# since they scoop up all remaining variables
#print(concat("earth", "mars", "venus")) # sep required
#print(concat("earth", "mars", "venus", "/")) # still sep required
print(concat("earth", "mars", "venus", sep="/")) # earth/mars/venus
def concat(*args, sep="/"):
return sep.join(args)
print(concat("earth", "mars", "venus")) # sep required
print(concat("earth", "mars", "venus", sep=".")) # still sep required
Unpacking Argument Lists
- Arguments in list or tuple and need to unpack for a function call requiring separate positional arguments
def add_numbers(n1=0, n2=0, n3=0, n4=0):
return n1+n2+n3+n4
print(add_numbers(1, 2, 3, 4)) #10
numbers = [1, 2, 3, 4]
print(add_numbers(*numbers)) # 10
numbers = {"n1":1, "n2":2, "n3":3, "n4":4} # dict for keywords
print(add_numbers(**numbers)) # 10
def add_numbers(n1, n2, n3, n4):
return n1+n2+n3+n4
print(add_numbers(1, 2, 3, 4)) #10
numbers = [1, 2, 3, 4]
print(add_numbers(*numbers)) # 10
numbers = {"n1":1, "n2":2, "n3":3, "n4":4} # dict for keywords
print(add_numbers(**numbers)) # 10
Lambda Expressions
- single expression but can refer to variables from the containing scope
f = lambda x: x + 1 # function
print(f(2)) # 3
f = lambda x, y: x + y # function
print(f(2,3)) # 5
# function return function
def make_incrementor(n): # create function with incrementor n
return lambda x: x + n
f1 = make_incrementor(1) # function increments 1
print(f1(3)) # 4
f5 = make_incrementor(5) # function increments 5
print(f5(3)) # 8
# Pass function as argument
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key = lambda pair: pair[1]) # sort based on index 1
print(pairs)
Documentation Strings
- The first line should always be a short, concise summary of the object’s purpose.
- It should not explicitly state the object’s name or type, since these are available by other means (except if the name happens to be a verb describing a function’s operation).
- This line should begin with a capital letter and end with a period.
- If there are more lines in the documentation string, the second line should be blank, visually separating the summary from the rest of the description.
- The Python parser does not strip indentation from multi-line string literals in Python, so tools that process documentation have to strip indentation if desired. This is done using the following convention.
- The first non-blank line after the first line of the string determines the amount of indentation for the entire documentation string. (We can’t use the first line since it is generally adjacent to the string’s opening quotes so its indentation is not apparent in the string literal.) Whitespace “equivalent” to this indentation is then stripped from the start of all lines of the string. Lines that are indented less should not occur, but if they occur all their leading whitespace should be stripped. Equivalence of whitespace should be tested after expansion of tabs (to 8 spaces, normally).
Intermezzo: Coding Style
- Use 4-space indentation and no tabs
- Wrap lines so that they don’t exceed 79 characters.
- This helps users with small displays and makes it possible to have several code files side-by-side on larger displays.
- Use blank lines to separate functions and classes, and larger blocks of code inside functions.
- When possible, put comments on a line of their own.
- Use docstrings.
- Use spaces around operators and after commas, but not directly inside bracketing constructs: a = f(1, 2) + g(3, 4).
- Name your classes and functions consistently; the convention is to use UpperCamelCase for classes and lowercase_with_underscores for functions and methods. Always use self as the name for the first method argument (see A First Look at Classes for more on classes and methods).
- Don’t use fancy encodings if your code is meant to be used in international environments. Python’s default, UTF-8, or even plain ASCII work best in any case.
- Likewise, don’t use non-ASCII characters in identifiers if there is only the slightest chance people speaking a different language will read or maintain the code.