Today: main(), list memory, 3x range() forms, nested loops, while/break logic
For details see guide: Python main() (uses affirm example too)
affirm.zip Example/exercise of main() command line args. You can do it yourself here, or just watch the examples below to see the ideas.
First run the code, see what the command line arguments do: -affirm and -hello options (aka "flags")
$ python3 affirm.py -affirm Lisa Everything is coming up Lisa $ python3 affirm.py -affirm Bart Looking good Bart $ python3 affirm.py -affirm Maggie Today is the day for Maggie $ python3 affirm.py -hello Bob Hello Bob $
-affirmCommand line arguments, or "args", are extra information typed on the line when a program is run. The system is deceptively simple - the command line arguments are the words typed after the program.py on the command line, separated from each other by spaces. So in this command line:
$ python3 affirm.py -affirm Lisa
The words -affirm and Lisa are the 2 command line args.
Command line arguments like -affirm often select a mode or option for the run of the program, and these options typically begin with a dash as we have here.
args Python ListIn main() args is set up as a Python list. Its contents is the 2 string form of the command line args.
$ python3 affirm.py -affirm Lisa
....
e.g. args = ['-affirm', 'Lisa']
$ python3 affirm.py -affirm Bart
....
e.g. args = ['-affirm', 'Bart']
Functions that do the work in this example, main() will call them. In this case the functions are quite simple, but the key pattern is that main() can call functions, passing in the right data as their parameters.
Functions to call:
def print_affirm(name):
"""
Given name, print a random affirmation for that name.
"""
affirmation = random.choice(AFFIRMATIONS)
print(affirmation, name)
def print_hello(name):
"""
Given name, print 'Hello' with that name.
"""
print('Hello', name)
def print_n_copies(n, name):
"""
Given int n and name, print n copies of that name.
"""
for i in range(n):
# Print each copy of the name with space instead of \n after it.
print(name, end=' ')
# Print a single \n after the whole thing.
print()
Make this command line work:
$ python3 affirm.py -affirm Lisa
Solution code
def main():
args = sys.argv[1:]
....
....
# 1. Check for the -affirm arg pattern:
# python3 affirm.py -affirm Bart
# e.g. args[0] is '-affirm' and args[1] is 'Bart'
if len(args) == 2 and args[0] == '-affirm':
print_affirm(args[1])
Write if-logic in main() to looking for the following command line form, call print_hello(name), passing in correct string.
$ python3 affirm.py -hello Bart
Solution code
if len(args) == 2 and args[0] == '-hello':
print_hello(args[1])
See Guide: Python Variables
>>> # Build up a list to have 3 string elements
>>> lst = []
>>> lst.append('yo')
>>> lst.append('yo')
>>> lst.append('ma')
>>> lst
['yo', 'yo', 'ma']
Here is the memory picture for this state. Each element in the list can hold anything. Here they each hold a pointer to a string. In this way, each is like a variable, and = can work on each one.
= is ShallowWhat does the following line do?
>>> lst2 = lst
The assignment = does not duplicate or makes a copy of a data structure. Instead the = works in a shallow way, just setting the pointer in the left variable to point to the same thing as the right variable.
Here is a picture after the assignment
>>> lst[2] 'ma' >>> lst2[2] 'ma'
>>> lst[2] = 'Donut'
Here is the memory picture after that change:
Now if we look at the value of lst or lst2 .. they both show the changed list, since in fact there is just the one list and they are both pointing to it.
>>> lst ['yo', 'yo', 'donut'] >>> lst2 ['yo', 'yo', 'donut']
This code sets up 2 lists of numbers
>>> evens = [2, 4] >>> odds = [1, 3, 5]
A list can be stored inside of an "outer" list. This code sets up the list outer: its first element is the "evens" list, its second element is the "odds" list.
>>> evens = [2, 4] >>> odds = [1, 3, 5] >>> outer = [] >>> outer.append(evens) >>> outer.append(odds) >>> >>> outer [[2, 4], [1, 3, 5]] >>> >>> outer[0] [2, 4] >>> outer[1] [1, 3, 5]
Here is a memory picture of how outer is built.
A classic way to "iterate" over numbers. All languages have some way to do this, and the Python range() functions are an easy and flexible way to do it.
# standard for/i/range structure
for i in range(20):
print(i)
range(5, 10) → [5, 6, 7, 8, 9] range(5, 6) → [5] range(5, 5) → [] # i.e. nothing range(5, 4) → [] # i.e. nothing
range(0, 10, 2) → [0, 2, 4, 6, 8] range(200, 800, 100) → [200, 300, 400, 500, 600, 700]
range(5, 0, -1) -> [5, 4, 3, 2, 1] range(10, 5, -2) -> [10, 8, 6] range(10, 10, -2) -> []
In interpreter, try these with list(range(xxx)) form which makes a [ ... ] list of the numbers, making the exact range of numbers very clear:
>>> list(range(10, 5, -2)) [10, 8, 6]
Given a non-negative int n, return a list of n multiples of 6 starting with 6 itself, e.g. n=3 returns [6, 12, 18]. -Given a non-negative int n, return a list of the first n multiples of 6, e.g. n=3 returns [6, 12, 18].
For practice, use reversed()
Given a non-negative int n, return a list of n multiples of 10 in decreasing order, so n = 4 returns [40, 30, 20, 10]
Given a non-negative int n. Create and return a list of lists like this: [[1], [1, 2], [1, 2, 3] .... [1, 2, 3, .. n]]. There will be n lists inside the outer list. Use nested range loops. One approach: write outer loop to set a "top" variable which varies the top number for each inner list.
Sketch output for n=3. Use this to think about outer and inner loops:
[ [1], [1, 2], [1. 2. 3], ]
Solution
def triangle(n):
outer = []
# Outer loop, one iteration per inner list
for top in range(1, n + 1):
# Inner loop, create 1 inner list
# 1..top
inner = []
for i in range(1, top + 1):
inner.append(i)
outer.append(inner)
return outer
Extra problem for practice.
Consider the sequence 100, 200, 300, ... n*100. For each 'x00' multiple of 100 in that sequence, make a list of the 10 numbers leading up to that number, e.g. [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]. Return a big list of all these lists as elements, first the list of 100, then 200, and so on. N = 2 yields [[91, 92, 93, 94, 95, 96, 97, 98, 99, 100], [191, 192, 193, 194, 195, 196, 197, 198, 199, 200]]. Use nested range loops.
> loop/break/parse problems
Censor problem: censored(n, censor): Given a non-negative int n. Return a list of the ints [1, 2, 3, ... n]. Except as soon as a number in the given censor list is seen, end the list without that number, so censored(10, [5, 4]) returns [1, 2, 3]. Use "break"
Solution
def censored(n, censor):
nums = []
for i in range(1, n + 1):
if i in censor: # Key: if-break
break
nums.append(i)
return nums
def censored(n, censor):
nums = []
for i in range(1, n + 1):
if i in censor:
i = n + 1 # let's be done with this loop
else:
result.append(i)
return nums
i = 0 # 1. init
while i < n: # 2. test
# use i
i += 1 # 3. increment, bottom of loop
double_char() written as a while (using a range() is easier for this problem, so just demonstrating what while would look like)
def while_double(s):
result = ''
i = 0
while i < len(s):
result += s[i] + s[i]
i += 1
return result
With 2 params, start index indicates where to begin scanning for the target. The default start index is 0
>>> s = 'xx[abc[xx'
>>> s.find('[') # start = 0, the default
2
>>> s.find('[', 2) # start = 2, no help
2
>>> s.find('[', 3) # start = 3, find next one
6
Given string s. Return a list of all the '[' strings in s. Use s.find() within a while loop to find all the '['.
Use search as index variable, marking current position of search within s - this will be a stereotypical pattern for searching through a string.
In loop - how to find each '['? How to end the loop when there is no '['? Important at bottom of loop, advance variable for next iteration ("search" in this case)
Solution
def all_lefts(s):
search = 0
result = []
while search < len(s):
left = s.find('[', search)
if left == -1:
break
result.append(s[left:left + 1])
# Key: set up var at end of loop
search = left + 1
return result
This pulls it all together, solving a realistic problem.
Given string s. Return a list of all the 'abc' strings for each '[abc]' substring within s. For each left bracket, find the right bracket that follows it. End the search if no left or right bracket is found. Use s.find() within a while loop.
Start with the all_brackets() code. Add code to find the right-bracket following each left bracket. As before: how to terminate the loop? Remember to advance search variable at bottom of loop.
Solution
def all_brackets(s):
search = 0
result = []
while search < len(s):
left = s.find('[', search)
if left == -1:
break
# Your code here
pass
right = s.find(']', left + 1)
if right == -1:
break
result.append(s[left + 1:right])
# Key: set up var at end of loop
search = right + 1
return result