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.
python3 affirm.py -affirm Phil
First run the code, see what the command line arguments do:
-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 $
Command 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
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 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()
argsis a Python list of the args typed on the command line
Make this command line work:
$ python3 affirm.py -affirm Lisa
len(args)- the number of args
args- the first arg
def main(): args = sys.argv[1:] .... .... # 1. Check for the -affirm arg pattern: # python3 affirm.py -affirm Bart # e.g. args is '-affirm' and args is 'Bart' if len(args) == 2 and args == '-affirm': print_affirm(args)
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
if len(args) == 2 and args == '-hello': print_hello(args)
See Guide: Python Variables
x = 7
x = y
=not copying a list
>>> # 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.
What does the following line do?
>>> lst2 = lst
= 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 'ma' >>> lst2 'ma'
lst, is like a little variable
>>> lst = 'Donut'
Here is the memory picture after that change:
Now if we look at the value of
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 [2, 4] >>> outer [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.
range(stop_num)# yields 0, 1, 2, .. stop_num-1
range(10)# yields 0, 1, 2, ... 9
range(0)- no numbers at all
# standard for/i/range structure for i in range(20): print(i)
reversed(range(n)) -> n-1, n-2 ...2, 1, 0
range(start, stop)# this form = 2 parameters
range(5, 10) → [5, 6, 7, 8, 9] range(5, 6) →  range(5, 5) →  # i.e. nothing range(5, 4) →  # i.e. nothing
range(start, stop, step)# this form = 3 parameters
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]
> nested list/range
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, 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, 2], [1. 2. 3], ]
i j kfor 3 loops
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"
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
for i in range(n)- go-to solution for that sequence
while.. do steps manually
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 '['.
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)
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.
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