Any questions that are nagging?
Ordered collections of objects
Remember Duck Typing? A sequence can be considered as anything that supports at least these operations:
There are seven builtin types in Python that are sequences:
For this class, you won’t see much beyond the string types, lists, tuples – the rest are pretty special purpose.
But what we say today applies to all sequences (with minor caveats)
Items in a sequence may be looked up by index using the subscription operator: []
Indexing in Python always starts at zero.
In [98]: s = u"this is a string"
In [99]: s[0]
Out[99]: u't'
In [100]: s[5]
Out[100]: u'i'
You can use negative indexes to count from the end:
In [105]: s = u"this is a string"
In [106]: s[-1]
Out[106]: u'g'
In [107]: s[-6]
Out[107]: u's'
Indexing beyond the end of a sequence causes an IndexError:
In [4]: s = [0, 1, 2, 3]
In [5]: s[4]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-5-42efaba84d8b> in <module>()
----> 1 s[4]
IndexError: list index out of range
Slicing a sequence creates a new sequence with a range of objects from the original sequence.
It also uses the subscription operator ([]), but with a twist.
sequence[start:finish] returns all sequence[i] for which start <= i < finish:
In [121]: s = u"a bunch of words"
In [122]: s[2]
Out[122]: u'b'
In [123]: s[6]
Out[123]: u'h'
In [124]: s[2:6]
Out[124]: u'bunc'
In [125]: s[2:7]
Out[125]: u'bunch'
Think of the indexes as pointing to the spaces between the items:
a b u n c h o f
| | | | | | | | | |
0 1 2 3 4 5 6 7 8 9
You do not have to provide both start and finish:
In [6]: s = u"a bunch of words"
In [7]: s[:5]
Out[7]: u'a bun'
In [8]: s[5:]
Out[8]: u'ch of words'
Either 0 or len(s) will be assumed, respectively.
You can combine this with the negative index to get the end of a sequence:
In [4]: s = u'this_could_be_a_filename.txt'
In [5]: s[:-4]
Out[5]: u'this_could_be_a_filename'
In [6]: s[-4:]
Out[6]: u'.txt'
Python indexing feels ‘weird’ to some folks – particularly those that don’t come with a background in the C family of languages.
Why is the “first” item indexed with zero?
Why is the last item in the slice not included?
Because these lead to some nifty properties:
len(seq[a:b]) == b-a
seq[:b] + seq[b:] == seq
len(seq[:b]) == b
len(seq[-b:]) == b
There are very many fewer “off by one” errors as a result.
Slicing takes a third argument, step which controls which items are returned:
In [289]: string = u"a fairly long string"
In [290]: string[0:15]
Out[290]: u'a fairly long s'
In [291]: string[0:15:2]
Out[291]: u'afil ogs'
In [292]: string[0:15:3]
Out[292]: u'aallg'
In [293]: string[::-1]
Out[293]: u'gnirts gnol ylriaf a'
Though they share an operator, slicing and indexing have a few important differences:
Indexing will always return one object, slicing will return a sequence of objects.
Indexing past the end of a sequence will raise an error, slicing will not:
In [129]: s = "a bunch of words"
In [130]: s[17]
----> 1 s[17]
IndexError: string index out of range
In [131]: s[10:20]
Out[131]: ' words'
In [132]: s[20:30]
Out[132]: "
(demo)
All sequences support the in and not in membership operators:
In [15]: s = [1, 2, 3, 4, 5, 6]
In [16]: 5 in s
Out[16]: True
In [17]: 42 in s
Out[17]: False
In [18]: 42 not in s
Out[18]: True
For strings, the membership operations are like substring operations in other languages:
In [20]: s = u"This is a long string"
In [21]: u"long" in s
Out[21]: True
This does not work for sub-sequences of other types (can you think of why?):
In [22]: s = [1, 2, 3, 4]
In [23]: [2, 3] in s
Out[23]: False
Using + or * on sequences will concatenate them:
In [25]: s1 = u"left"
In [26]: s2 = u"right"
In [27]: s1 + s2
Out[27]: u'leftright'
In [28]: (s1 + s2) * 3
Out[28]: u'leftrightleftrightleftright'
You can apply this concatenation to slices as well, leading to some nicely concise code:
from CodingBat: Warmup-1 – front3
def front3(str):
if len(str) < 3:
return str+str+str
else:
return str[:3]+str[:3]+str[:3]
This non-pythonic solution can also be expressed like so:
def front3(str):
return str[:3] * 3
All sequences have a length. You can get it with the len builtin:
In [36]: s = u"how long is this, anyway?"
In [37]: len(s)
Out[37]: 25
Remember, Python sequences are zero-indexed, so the last index in a sequence is len(s) - 1:
In [38]: count = len(s)
In [39]: s[count]
------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-39-5a33b9d3e525> in <module>()
----> 1 s[count]
IndexError: string index out of range
Even better: use s[-1]
There are a more operations supported by all sequences
All sequences also support the min and max builtins:
In [42]: all_letters = u"thequickbrownfoxjumpedoverthelazydog"
In [43]: min(all_letters)
Out[43]: u'a'
In [44]: max(all_letters)
Out[44]: u'z'
Why are those the answers you get? (hint: ord(u'a'))
All sequences also support the index method, which returns the index of the first occurence of an item in the sequence:
In [46]: all_letters.index(u'd')
Out[46]: 21
This causes a ValueError if the item is not in the sequence:
In [47]: all_letters.index(u'A')
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-47-2db728a46f78> in <module>()
----> 1 all_letters.index(u'A')
ValueError: substring not found
A sequence can also be queried for the number of times a particular item appears:
In [52]: all_letters.count(u'o')
Out[52]: 4
In [53]: all_letters.count(u'the')
Out[53]: 2
This does not raise an error if the item you seek is not present:
In [54]: all_letters.count(u'A')
Out[54]: 0
More on this in a while.
The other sequence types.
Lists can be constructed using list Literals ([]):
In [1]: []
Out[1]: []
In [2]: [1,2,3]
Out[2]: [1, 2, 3]
In [3]: [1, 'a', 7.34]
Out[3]: [1, 'a', 7.34]
Or by using the list type object as a constructor:
In [6]: list()
Out[6]: []
In [7]: list(range(4))
Out[7]: [0, 1, 2, 3]
In [8]: list('abc')
Out[8]: ['a', 'b', 'c']
The elements contained in a list need not be of a single type.
Lists are heterogenous, ordered collections.
Each element in a list is a value, and can be in multiple lists and have multiple names (or no name)
In [9]: name = u'Brian'
In [10]: a = [1, 2, name]
In [11]: b = [3, 4, name]
In [12]: a[2]
Out[12]: u'Brian'
In [13]: b[2]
Out[13]: u'Brian'
In [14]: a[2] is b[2]
Out[14]: True
Tuples can be constructed using tuple literals (()):
In [15]: ()
Out[15]: ()
In [16]: (1, 2)
Out[16]: (1, 2)
In [17]: (1, 'a', 7.65)
Out[17]: (1, 'a', 7.65)
In [18]: (1,)
Out[18]: (1,)
Tuples don’t NEED parentheses...
In [161]: t = (1,2,3)
In [162]: t
Out[162]: (1, 2, 3)
In [163]: t = 1,2,3
In [164]: t
Out[164]: (1, 2, 3)
In [165]: type(t)
Out[165]: tuple
But they do need commas...!
In [156]: t = ( 3 )
In [157]: type(t)
Out[157]: int
In [158]: t = (3,)
In [160]: type(t)
Out[160]: tuple
You can also use the tuple type object to convert any sequence into a tuple:
In [20]: tuple()
Out[20]: ()
In [21]: tuple(range(4))
Out[21]: (0, 1, 2, 3)
In [22]: tuple('garbanzo')
Out[22]: ('g', 'a', 'r', 'b', 'a', 'n', 'z', 'o')
The elements contained in a tuple need not be of a single type.
Tuples are heterogenous, ordered collections.
Each element in a tuple is a value, and can be in multiple tuples and have multiple names (or no name)
In [23]: name = u'Brian'
In [24]: other = name
In [25]: a = (1, 2, name)
In [26]: b = (3, 4, other)
In [27]: for i in range(3):
....: print(a[i] is b[i], end=' ')
....:
False False True
So Why Have Both?
image from flickr by illuminaut, (CC by-nc-sa)
All objects in Python fall into one of two camps:
Objects which are mutable may be changed in place.
Objects which are immutable may not be changed.
Immutable | Mutable |
---|---|
Unicode | List |
String | |
Integer | |
Float | |
Tuple |
Try this out:
In [28]: food = [u'spam', u'eggs', u'ham']
In [29]: food
Out[29]: [u'spam', u'eggs', u'ham']
In [30]: food[1] = u'raspberries'
In [31]: food
Out[31]: [u'spam', u'raspberries', u'ham']
And repeat the exercise with a Tuple:
In [32]: food = (u'spam', u'eggs', u'ham')
In [33]: food
Out[33]: (u'spam', u'eggs', u'ham')
In [34]: food[1] = u'raspberries'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-34-0c3401794933> in <module>()
----> 1 food[1] = u'raspberries'
TypeError: 'tuple' object does not support item assignment
This property means you need to be aware of what you are doing with your lists:
In [36]: original = [1, 2, 3]
In [37]: altered = original
In [38]: for i in range(len(original)):
....: if True:
....: altered[i] += 1
....:
Perhaps we want to check to see if altered has been updated, as a flag for whatever condition caused it to be updated.
What is the result of this code?
Our altered list has been updated:
In [39]: altered
Out[39]: [2, 3, 4]
But so has the original list:
In [40]: original
Out[40]: [2, 3, 4]
Why?
Easy container setup, or deadly trap?
(note: you can nest lists to make a 2D-ish array)
In [13]: bins = [ [] ] * 5
In [14]: bins
Out[14]: [[], [], [], [], []]
In [15]: words = [u'one', u'three', u'rough', u'sad', u'goof']
In [16]: for word in words:
....: bins[len(word)-1].append(word)
....:
So, what is going to be in bins now?
In [65]: bins
Out[65]:
[[u'one', u'three', u'rough', u'sad', u'goof'],
[u'one', u'three', u'rough', u'sad', u'goof'],
[u'one', u'three', u'rough', u'sad', u'goof'],
[u'one', u'three', u'rough', u'sad', u'goof'],
[u'one', u'three', u'rough', u'sad', u'goof']]
We multiplied a sequence containing a single mutable object.
We got a list containing five pointers to a single mutable object.
Watch out especially for passing mutable objects as default values for function parameters:
In [71]: def accumulator(count, list=[]):
....: for i in range(count):
....: list.append(i)
....: return list
....:
In [72]: accumulator(5)
Out[72]: [0, 1, 2, 3, 4]
In [73]: accumulator(7)
Out[73]: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6]
In addition to all the methods supported by sequences we’ve seen above, mutable sequences (the List), have a number of other methods that are used to change the list.
You can find all these in the Standard Library Documentation:
http://docs.python.org/2/library/stdtypes.html#mutable-sequence-types
You’ve already seen changing a single element of a list by assignment.
Pretty much the same as “arrays” in most languages:
In [100]: list = [1, 2, 3]
In [101]: list[2] = 10
In [102]: list
Out[102]: [1, 2, 10]
.append(), .insert(), .extend()
In [74]: food = [u'spam', u'eggs', u'ham']
In [75]: food.append(u'sushi')
In [76]: food
Out[76]: [u'spam', u'eggs', u'ham', u'sushi']
In [77]: food.insert(0, u'beans')
In [78]: food
Out[78]: [u'beans', u'spam', u'eggs', u'ham', u'sushi']
In [79]: food.extend([u'bread', u'water'])
In [80]: food
Out[80]: [u'beans', u'spam', u'eggs', u'ham', u'sushi', u'bread', u'water']
You can pass any sequence to .extend():
In [85]: food
Out[85]: [u'beans', u'spam', u'eggs', u'ham', u'sushi', u'bread', u'water']
In [86]: food.extend(u'spaghetti')
In [87]: food
Out[87]:
[u'beans', u'spam', u'eggs', u'ham', u'sushi', u'bread', u'water',
u's', u'p', u'a', u'g', u'h', u'e', u't', u't', u'i']
.pop(), .remove()
In [203]: food = ['spam', 'eggs', 'ham', 'toast']
In [204]: food.pop()
Out[204]: 'toast'
In [205]: food.pop(0)
Out[205]: 'spam'
In [206]: food
Out[206]: ['eggs', 'ham']
In [207]: food.remove('ham')
In [208]: food
Out[208]: ['eggs']
You can also delete slices of a list with the del keyword:
In [92]: nums = range(10)
In [93]: nums
Out[93]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [94]: del nums[1:6:2]
In [95]: nums
Out[95]: [0, 2, 4, 6, 7, 8, 9]
In [96]: del nums[-3:]
In [97]: nums
Out[97]: [0, 2, 4, 6]
You can make copies of part of a list using slicing:
In [227]: food = ['spam', 'eggs', 'ham', 'sushi']
In [228]: some_food = food[1:3]
In [229]: some_food[1] = 'bacon'
In [230]: food
Out[230]: ['spam', 'eggs', 'ham', 'sushi']
In [231]: some_food
Out[231]: ['eggs', 'bacon']
If you provide no arguments to the slice, it makes a copy of the entire list:
In [232]: food
Out[232]: ['spam', 'eggs', 'ham', 'sushi']
In [233]: food2 = food[:]
In [234]: food is food2
Out[234]: False
The copy of a list made this way is a shallow copy.
The list is itself a new object, but the objects it contains are not.
Mutable objects in the list can be mutated in both copies:
In [249]: food = ['spam', ['eggs', 'ham']]
In [251]: food_copy = food[:]
In [252]: food[1].pop()
Out[252]: 'ham'
In [253]: food
Out[253]: ['spam', ['eggs']]
In [256]: food.pop(0)
Out[256]: 'spam'
In [257]: food
Out[257]: [['eggs']]
In [258]: food_copy
Out[258]: ['spam', ['eggs']]
Consider this common pattern:
for x in somelist:
if should_be_removed(x):
somelist.remove(x)
This looks benign enough, but changing a list while you are iterating over it can be the cause of some pernicious bugs.
For example:
In [121]: list = range(10)
In [122]: list
Out[122]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [123]: for x in list:
.....: list.remove(x)
.....:
What is the expected outcome of this code?
In [124]: list
Out[124]: [1, 3, 5, 7, 9]
Was that what you expected?
Iterate over a copy, and mutate the original:
In [126]: list = range(10)
In [127]: for x in list[:]:
.....: list.remove(x)
.....:
In [128]: list
Out[128]: []
Okay, so we’ve done this a bunch already, but let’s state it out loud.
You can iterate over a sequence.
for element in sequence:
do_something(element)
Again, we’ll touch more on this in a short while, but first a few more words about Lists and Tuples.
These methods change a list in place and are not available on immutable sequence types.
.reverse()
In [129]: food = [u'spam', u'eggs', u'ham']
In [130]: food.reverse()
In [131]: food
Out[131]: [u'ham', u'eggs', u'spam']
.sort()
In [132]: food.sort()
In [133]: food
Out[133]: [u'eggs', u'ham', u'spam']
Because these methods mutate the list in place, they have a return value of None
.sort() can take an optional key parameter.
It should be a function that takes one parameter (list items one at a time) and returns something that can be used for sorting:
In [137]: def third_letter(string):
.....: return string[2]
.....:
In [138]: food.sort(key=third_letter)
In [139]: food
Out[139]: [u'spam', u'eggs', u'ham']
Here are a few guidelines on when to choose a list or a tuple:
Otherwise ... taste and convention
Lists are Collections (homogeneous): – contain values of the same type – simplifies iterating, sorting, etc
tuples are mixed types: – Group multiple values into one logical thing – Kind of like simple C structs.
For more information, read the list docs:
http://docs.python.org/2/library/stdtypes.html#mutable-sequence-types
(actually any mutable sequence....)
Repetition, Repetition, Repetition, Repe...
We’ve seen simple iteration over a sequence with for ... in:
In [170]: for x in "a string":
.....: print(x)
.....:
a
s
t
r
i
n
g
Contrast this with other languages, where you must build and use an index:
for(var i=0; i<arr.length; i++) {
var value = arr[i];
alert(i + ") " + value);
If you need an index, though you can use enumerate:
In [140]: for idx, letter in enumerate(u'Python'):
.....: print(idx, letter, end=' ')
.....:
0 P 1 y 2 t 3 h 4 o 5 n
The range builtin is useful for looping a known number of times:
In [171]: for i in range(5):
.....: print(i)
.....:
0
1
2
3
4
But you don’t really need to do anything at all with i
Be alert that a loop does not create a local namespace:
In [172]: x = 10
In [173]: for x in range(3):
.....: pass
.....:
In [174]: x
Out[174]: 2
Sometimes you want to interrupt or alter the flow of control through a loop.
Loops can be controlled in two ways, with break and continue
The break keyword will cause a loop to immediately terminate:
In [141]: for i in range(101):
.....: print(i)
.....: if i > 50:
.....: break
.....:
0 1 2 3 4 5... 46 47 48 49 50 51
The continue keyword will skip later statements in the loop block, but allow iteration to continue:
In [143]: for in in range(101):
.....: if i > 50:
.....: break
.....: if i < 25:
.....: continue
.....: print(i),
.....:
25 26 27 28 29 ... 41 42 43 44 45 46 47 48 49 50
For loops can also take an optional else block.
Executed only when the loop exits normally (not via break):
In [147]: for x in range(10):
.....: if x == 11:
.....: break
.....: else:
.....: print(u'finished')
finished
In [148]: for x in range(10):
.....: if x == 5:
.....: print(x)
.....: break
.....: else:
.....: print(u'finished')
5
This is a really nice unique Python feature!
The while keyword is for when you don’t know how many loops you need.
It continues to execute the body until condition is not True:
while a_condition:
some_code
in_the_body
while is more general than for
– you can always express for as while,
but not always vice-versa.
while is more error-prone – requires some care to terminate
loop body must make progress, so condition can become False
potential error – infinite loops:
i = 0;
while i < 5:
print(i)
Use break:
In [150]: while True:
.....: i += 1
.....: if i > 10:
.....: break
.....: print(i, end=' ')
.....:
1 2 3 4 5 6 7 8 9 10
Set a flag:
In [156]: import random
In [157]: keep_going = True
In [158]: while keep_going:
.....: num = random.choice(range(5))
.....: print(num)
.....: if num == 3:
.....: keep_going = False
.....:
3
Use a condition:
In [161]: while i < 10:
.....: i += random.choice(range(4))
.....: print(i)
.....:
0 0 2 3 4 6 8 8 8 9 12
Both for and while loops can use break and continue for internal flow control.
Both for and while loops can have an optional else block
In both loops, the statements in the else block are only executed if the loop terminates normally (no break)
Fun with Strings
split and join:
In [167]: csv = "comma, separated, values"
In [168]: csv.split(', ')
Out[168]: ['comma', 'separated', 'values']
In [169]: psv = '|'.join(csv.split(', '))
In [170]: psv
Out[170]: 'comma|separated|values'
In [171]: sample = u'A long string of words'
In [172]: sample.upper()
Out[172]: u'A LONG STRING OF WORDS'
In [173]: sample.lower()
Out[173]: u'a long string of words'
In [174]: sample.swapcase()
Out[174]: u'a LONG STRING OF WORDS'
In [175]: sample.title()
Out[175]: u'A Long String Of Words'
In [181]: number = u"12345"
In [182]: number.isnumeric()
Out[182]: True
In [183]: number.isalnum()
Out[183]: True
In [184]: number.isalpha()
Out[184]: False
In [185]: fancy = u"Th!$ $tr!ng h@$ $ymb0l$"
In [186]: fancy.isalnum()
Out[186]: False
“ASCII” values: 1-127
“ANSI” values: 1-255
To get the value:
In [109]: for i in 'Chris':
.....: print(ord(i), end=' ')
67 104 114 105 115
In [110]: for i in (67,104,114,105,115):
.....: print(chr(i), end=' ')
C h r i s
You can, but please don’t do this:
'Hello ' + name + '!'
Do this instead:
'Hello %s!' % name
It’s much faster and safer, and easier to modify as code gets complicated.
http://docs.python.org/library/stdtypes.html#string-formatting-operations
The string format operator: %
In [261]: u"an integer is: %i" % 34
Out[261]: u'an integer is: 34'
In [262]: u"a floating point is: %f" % 34.5
Out[262]: u'a floating point is: 34.500000'
In [263]: u"a string is: %s" % u"anything"
Out[263]: u'a string is: anything'
Multiple placeholders:
In [264]: u"the number %s is %i" % (u'five', 5)
Out[264]: u'the number five is 5'
In [266]: u"the first 3 numbers are: %i, %i, %i" % (1,2,3)
Out[266]: u'the first 3 numbers are: 1, 2, 3'
The counts must agree:
In [187]: u"string with %i formatting %s" % (1, )
---------------------------------------------------------------------------
...
TypeError: not enough arguments for format string
Named placeholders:
In [191]: u"Hello, %(name)s, whaddaya know?" % {u'name': "Joe"}
Out[191]: u'Hello, Joe, whaddaya know?'
You can use values more than once, and skip values:
In [193]: u"Hi, %(name)s. Howzit, %(name)s?" % {u'name': u"Bob", u'age': 27}
Out[193]: u'Hi, Bob. Howzit, Bob?'
In more recent versions of Python (2.6+) this is being phased out in favor of the .format() method on strings.
In [194]: u"Hello, {}, how's your {}".format(u"Bob", u"wife")
Out[194]: u"Hello, Bob, how's your wife"
In [195]: u"Hi, {name}. How's your {relation}?".format(name=u'Bob', relation=u'wife')
Out[195]: u"Hi, Bob. How's your wife?"
For both of these forms of string formatting, there is a complete syntax for specifying all sorts of options.
It’s well worth your while to spend some time getting to know this formatting language. You can accomplish a great deal just with this.
For some of your homework, you’ll need to interact with a user at the command line.
There’s a nice builtin function to do this - raw_input:
In [196]: fred = raw_input(u'type something-->')
type something-->;alksdjf
In [197]: fred
Out[197]: ';alksdjf'
This will display a prompt to the user, allowing them to input text and allowing you to bind that input to a symbol.
List Lab (after http://www.upriss.org.uk/python/session5.html)
In your student folder, create a subdirectory called session03. Create a new branch called task6 and switch to it (git checkout task6).
Within the session03 subdirectory, create a new file called list_lab.py.
The file should be an executable python script. That is to say that one should be able to run the script directly like so:
$ ./list_lab.py
Remember the hash-bang (#!/usr/bin/env python)!
Add the file to your clone of the repository and commit changes frequently while working on the following tasks. When you are done, push your changes to GitHub and issue a pull request.
When the script is run, it should accomplish the following four series of actions:
Using the list created in series 1 above:
Again, using the list from series 1:
Once more, using the list from series 1:
Commit and push to the task6 branch, then submit a pull request to the main class repo.
Finally, submit your assignment in Canvas by giving the URL of the pull request.
Mail Room
This is an abstract task. Given a list of goals for a software project, you create a plan for completing these goals. After next session, you’ll program your plan.
To accomplish this task, you’ll need to write some pseudocode. Like this:
def get_user_input(prompt, validator=None):
"""request input from the user with `prompt` and return the result
optionally, validate the input with a function `validator` which must
take one argument, the input from the user and must return the input if
valid, and None if not valid
"""
reply = None
while reply is None:
reply = ask_for_input(prompt)
if there_is_a_validator:
validate_the_reply
return reply
You work in the mail room at a local charity. Part of your job is to write incredibly boring, repetitive emails thanking your donors for their generous gifts. You are tired of doing this over an over again, so you’ve decided to let Python help you out of a jam.
You want to write a small command-line script that can handle some of the tasks associated with this job for you. Here’s a list of the things you want to be able to do:
It is fine to forget new donors once the script quits running.
Begin by thinking of each individual step as a stand-alone operation. What kind of function would you need to write to accomplish a single step from the above list of steps?
Write a series of pseudocode functions to accomplish the tasks identified by the list of steps above.
Continue by planning the flow of your script. What should happen first, second? How will you move from one step to the next?
You may find that creating a flow chart that shows how you expect your script to work can help you to visualize this process.
You can use a program to create a flow chart, or a free web service like draw.io. Or you can simply sketch something by hand.
To submit this homework, attach a file containing your pseudocode functions and a screenshot of your flow chart to the submission page in canvas.
We will discuss the implementation of this program in more detail during session 4, so make sure you think this through so you can participate in that discussion.
Read through the Session 04 slides.
http://codefellows.github.io/sea-c34-python/session04.html
There are four sections. For each one, come up with the following numbers of questions.
Write some Python code to help you answer them, one function per question.
For each function, write a good docstring describing what question you are trying to answer.
Put the functions in four separate modules (files) called dictionaries.py, exceptions.py, files.py, paths.py in the session04 subdirectory of your student directory, just as you did for list_lab.py up above.
That is, you should have nine questions, and nine functions, total, spread out across four files.
Use everything you’ve learned so far (including lists, tuples, slicing, iteration, functions, booleans, and printing).
Create a branch in your local repo called task8 and switch to it (git checkout task8).
Add your files to that branch, commit and push, then submit a pull request to the main class repo.
Finally, submit your assignment in Canvas by giving the URL of the pull request.