Lists and Loops¶
A New Sequence: Lists¶
Recall that sequences are an abstract type in Python that represent ordered collections of elements: e.g., strings, lists, range objects, etc.
Last lecture we focused on strings, today we will discuss lists.
Unlike strings, which are a homogenous sequence of characters, lists can be a collection of heterogenous objects.
# Examples of various lists:
wordList = ['What', 'a', 'beautiful', 'day']
numList = [1, 5, 8, 9, 15, 27]
charList = ['a', 'e', 'i', 'o', 'u']
mixedList = [3.145, 'hello', 13, True] # lists can be heterogeous
type(numList)
list
Sequence Operators and Functions¶
We saw several operators and functions that apply to sequences when we discussed strings. They apply to lists as well.
Indexing elements of lists using
[]
Using
len
function to find lengthSlicing lists using [:]
Testing membership using
in
operatorConcatenation using
+
wordList = ['What', 'a', 'beautiful', 'day']
wordList[3]
'day'
wordList[-1]
'day'
len(wordList)
4
nameList = ["Anna", "Beth", "Chris", "Daxi", "Emory", "Fatima"]
nameList[2:4]
['Chris', 'Daxi']
wordList + nameList # concetenate lists
['What',
'a',
'beautiful',
'day',
'Anna',
'Beth',
'Chris',
'Daxi',
'Emory',
'Fatima']
wordList[::-1] # what will this do?
['day', 'beautiful', 'a', 'What']
nameList = ["Aamir", "Beth", "Chris", "Daxi", "Emory"]
"Anna" in nameList # test membership
False
"Jeannie" in nameList
False
"Jeannie" not in nameList # not in returns true if el not in seq
True
"a" not in "Chris"
True
not in
operator for sequences:¶
The in
operator tests membership and returns True
if and only if an element is in a sequence. On the other hand, the not in
operator returns True
if and only if a given element is not in the sequence.
It is preferable and more readable to say if el not in seq
compared to the (logically equivalent) if not el in seq
.
Strings to Lists and Vice Versa¶
It is often useful to be able to convert strings to lists, and lists to strings.
.split()
method. When the split()
method is called on a string of words, it breaks up the words (by default at blank spaces) and returns a list of the words
.join()
method. Given a list of strings, the .join()
string method, when applied to a character char
, concatenates the strings in the list together with the character char
between them and returns a string
phrase = "What a lovely day"
phrase.split()
['What', 'a', 'lovely', 'day']
newPhrase = "What a *lovely* day!" # multiple spaces or punctuations dont matter
newPhrase.split()
['What', 'a', '*lovely*', 'day!']
commaSepSpells = "Impervius, Portus, Lumos, Reducio, Protego" #comma separated strings
commaSepSpells.split(',')
['Impervius', ' Portus', ' Lumos', ' Reducio', ' Protego']
wordList = ['Everybody', 'is', 'looking', 'forward', 'to', 'the', 'weekend']
'*'.join(wordList)
'Everybody*is*looking*forward*to*the*weekend'
'_'.join(wordList)
'Everybody_is_looking_forward_to_the_weekend'
' '.join(wordList)
'Everybody is looking forward to the weekend'
Looping over Lists¶
We can use a for
loop to iterate over the elements of a list.
numList = [0, 2, 4, 6, 8, 10]
for num in numList:
print(num)
0
2
4
6
8
10
Exercise: countItem
function¶
Write a function countItem
that takes as input a sequence seq
(can be a string or a list), and an element el
and returns the number of times el
appears in the sequence seq
.
def countItem(seq, el):
"""Takes list of sequence seq, and returns
the number of times el appears"""
pass
countItem([3, 2, 2, 3, 2, 1, 4], 2)
countItem("onomatopoeia", "o")
countItem("Alabama", "a")
countItem(["apple", "banana", "orange"], "kiwi")
List Accumulations¶
It is often the case that we use loops to iterate over a sequence to accumulate certain items from it. For example, we may want to collect all the words from a wordList that begin with a vowel (or have some other interesting property). How would we approach this problem?
First we need to be able to iterate over all words in the original list.
Then, for each word we must check if it has the property we are looking for.
If it does, we want to remember it: by storing it somewhere.
Since we want to store a collection of such words, we can use a list type. If we were interested in returning a string (e.g., in
vowelSeq
, we would collect the characters in a string accumulation variable.)
Such processes where we are accumulating something while iterating over sequences call for the use of an appropriate accumulation variable. It is important to initialize these accumulation variables before the loop.
Exercise: wordStartEnd
¶
Write a function that iterates over a given list of words wordList
, returns a (new) list containing all the words in wordList
that start and end with the same letter (ignoring case).
Expected behavior:
>>> wordStartEnd(['Anna', 'banana', 'salad', 'Rigor', 'tacit', 'hope'])
['Anna', 'Rigor', 'tacit']
>>> wordStartEnd(['New York', 'Tokyo', 'Paris'])
[]
>>> wordStartEnd(['*Hello*', '', 'nope'])
['*Hello*']
def wordStartEnd(wordList):
'''Takes a list of words and returns the list of words in it
that start and end with the same letter'''
pass
def wordStartEnd(wordList):
'''Takes a list of words and returns the list of words in it
that start and end with the same letter'''
# initialize accumulation variable (of type list)
result = []
for word in wordList: # iterate over list
#check for empty strings before indexing
if len(word) != 0:
if word[0].lower() == word[-1].lower():
result += [word] # concatenate to resulting list
return result # notice the indentation of return
wordStartEnd(['Anna', 'banana', 'salad', 'Rigor', 'tacit', 'hope'])
['Anna', 'Rigor', 'tacit']
wordStartEnd(['Scared', 'Sure', 'Sars', 'Viral', 'viv', 'stills'])
['Sars', 'viv', 'stills']
wordStartEnd(['New York', 'Tokyo', 'Paris'])
[]
wordStartEnd(['*Hello*', '', 'nope'])
['*Hello*']
wordStartEnd(['Exude', 'Eerie', 'Soup', 'knack', 'snack'])
['Exude', 'Eerie', 'knack']
Exercise: palindromes
¶
Write a function that iterates over a given list of strings sList
, returns a (new) list containing all the strings in sList
that are palindromes (read the same backward and forward).
Expected behavior:
>>> palindromes(['Anna', 'banana', 'kayak', 'rigor', 'tacit', 'hope'])
['Anna', 'kayak']
>>> palindromes(['1313', '1110111', '0101'])
['1110111']
>>> wordStartEnd(['Level', 'Stick', 'Gag'])
['Level', 'Gag']
def palindromes(sList):
'''Takes a list of words and counts the
number of words in it that start and end
with the same letter'''
pass
def palindromes(sList):
'''Takes a list of words and counts the
number of words in it that start and end
with the same letter'''
# initialize accumulation variable (of type list)
result = []
for word in sList: # iterate over list
wLower = word.lower()
if wLower[::-1] == wLower:
result += [word] # concatenate to resulting list
return result
palindromes(['Anna', 'banana', 'kayak', 'rigor', 'tacit', 'hope'])
['Anna', 'kayak']
palindromes(['1313', '1110111', '0101'])
['1110111']
palindromes(['Level', 'Stick', 'Gag'])
['Level', 'Gag']
Testing Functions¶
Suppose we want to test a function we have written. There are several ways to do so. You can test using interactively python by importing the function from checking to see if it returns the correct output when called on a bunch of different values.
Testing using doctests. Python’s doctest module allows you to embed your test cases and expected output directly into a functions docstring. To use the doctest module we must import it. To make sure the test cases are run when the program is run as a script from the terminal, we need to insert a doctest.testmod(). To make sure that the tests are not run in an interactive shell or when the functions from the module are imported, we should place the command within a guarded if __name__ == "__main__":
block. See slides for more explanation.
Doctests module¶
The doctest
module searches for pieces of text that look like interactive Python sessions, and then executes those sessions to verify that they work exactly as shown.
def isVowel(char):
"""Predicate that returns true only when a letter is a vowel.
>>> isVowel('d')
False
>>> isVowel('e')
True
"""
return char.lower() in 'aeiou'
import doctest
doctest.testmod(verbose = True)
# Task: try this out as a script and
# run from the terminal
Trying:
isVowel('d')
Expecting:
False
ok
Trying:
isVowel('e')
Expecting:
True
ok
4 items had no tests:
__main__
__main__.countItem
__main__.palindromes
__main__.wordStartEnd
1 items passed all tests:
2 tests in __main__.isVowel
2 tests in 5 items.
2 passed and 0 failed.
Test passed.
TestResults(failed=0, attempted=2)
Another Sequence: Range function¶
Python provides an easy way to iterate over common numerical sequences through the range
function.
In Python 3, we don’t have an easy way to display the numbers stored in a range object. If we want to examine the contents of a range object, we may pass the object into the function list() which returns a list of the numbers in the range object.
range(0,10)
range(0, 10)
type(range(0, 10))
range
List Type¶
Similar to other types that we have seen: integer, float and string, there is a built-in function with the name of the type that generates values of that types by converting from a type to another.
list()
function on range objects.
The list()
function when given a range object, returns a list of the elements in that range.
list(range(0, 10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Notice. The range(firNum, secNum)
represents all numbers from firNum
through secNum - 1.
If the firNum
is 0, we can omit. For example:
list(range(-10, 10))
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Using list()
on strings.
Converts strings to a list of string characters. The list
function, if given a string, returns a list of all its constituent characters (in order).
spell = "Avada Kedavra!"
list(spell) # can turn a string into a list of its characters
['A', 'v', 'a', 'd', 'a', ' ', 'K', 'e', 'd', 'a', 'v', 'r', 'a', '!']
list(str(3.14159265))
['3', '.', '1', '4', '1', '5', '9', '2', '6', '5']
Looping over ranges¶
Range functions provides us with an iterable sequence, which we can loop over, just like we did with strings and list.
What are some reasons we might want to loop over a range of numbers?
for i in range(1, 11): # simple for loop that prints numbers 1-10
print(i)
1
2
3
4
5
6
7
8
9
10
for i in range(5): # for loops to print patterns
print('$' * i)
for j in range(5):
print('*' * j)
$
$$
$$$
$$$$
*
**
***
****