Loops and Files Answers
Loop Exercises
Hint
If you get stuck for a minute or more, try searching Google or using help.
If you’re stuck for more than a few minutes, some of these links might be helpful for some of the exercises below:
Starting with a vowel
File: Edit the get_vowel_names function in the loops.py file that is in the exercises directory.
Test: Run python test.py get_vowel_names in your exercises directory.
Exercise: Make a function that accepts a list of names and returns a new list containing all names that start with a vowel. To test the function in the REPL, you can paste the function in from your text editor or import it as shown:
>>> from loops import get_vowel_names
>>> names = ["Alice", "Bob", "Christy", "Jules"]
>>> get_vowel_names(names)
['Alice']
>>> names = ["Scott", "Arthur", "Jan", "elizabeth"]
>>> get_vowel_names(names)
['Arthur', 'elizabeth']
Note
Want to test this out manually (instead of using python test.py)?
You could could create a file called get_vowel_names_test.py with your own test code:
from loops import get_vowel_names
print('Calling get_vowel_names(["Alice", "Bob", "Christy", "Jules"])')
print("Expected: ['Alice']")
print(" Actual:", get_vowel_names(["Alice", "Bob", "Christy", "Jules"]))
print()
print('Calling get_vowel_names(["Scott", "Arthur", "Jan", "elizabeth"])')
print("Expected: ['Arthur', 'elizabeth']")
print(" Actual:", get_vowel_names(["Scott", "Arthur", "Jan", "elizabeth"]))
Then you can run that file to test your code:
$ python get_vowel_names_test.py
Answers
With a list of lowercase vowels:
def get_vowel_names(names):
"""Return a list containing all names given that start with a vowel."""
vowel_names = []
for name in names:
if name[0].lower() in ['a', 'e', 'i', 'o', 'u']:
vowel_names.append(name)
return vowel_names
Using startswith:
def get_vowel_names(names):
"""Return a list containing all names given that start with a vowel."""
vowel_names = []
vowels = ('a', 'e', 'i', 'o', 'u')
for name in names:
if name[0].lower().startswith(vowels):
vowel_names.append(name)
return vowel_names
With a set of vowels:
def get_vowel_names(names):
"""Return a list containing all names given that start with a vowel."""
vowel_names = []
vowels = set('aeiou')
for name in names:
if name[0].lower() in vowels:
vowel_names.append(name)
return vowel_names
With a string of all vowels:
def get_vowel_names(names):
"""Return a list containing all names given that start with a vowel."""
vowel_names = []
for name in names:
if name[0] in 'aeiouAEIOU':
vowel_names.append(name)
return vowel_names
With a string of lowercase vowels:
def get_vowel_names(names):
"""Return a list containing all names given that start with a vowel."""
vowel_names = []
for name in names:
if name[0].lower() in 'aeiou':
vowel_names.append(name)
return vowel_names
Average
File: Create a file average.py in the modules sub-directory of the exercises directory.
Test: Run python test.py average.py from your exercises directory.
Exercise: Make a program average.py that calculates the average of all given command-line arguments. To run it without using the test framework, see the example below. Be sure to move back to the exercises directory to run the test program.
To test it locally:
$ cd modules
$ python average.py 2 3 4 5 6 7
Average is 4.5
$ python average.py 2 3 4
Average is 3.0
Hint
You can get all command line arguments except the program filename with this trick:
import sys
arguments = sys.argv[1:]
We’ll learn about that [1:] syntax later.
Answers
import sys
arguments = sys.argv[1:]
total = 0
for num in arguments:
total += float(num)
print(f"Average is {total/len(arguments)}")
Later we will see better ways to solve this.
Sum Timestamps
This is the sum_timestamps exercise in loops.py.
File: Edit the sum_timestamps function in the loops.py file that is in the exercises directory.
Test: Run python test.py sum_timestamps in your exercises directory.
Exercise: The sum_timestamps function takes a list of timestamp strings of “minute:seconds”, and returns a “minute:seconds” timestamp string that represents the total time from the list of timestamps.
Feel free to use the exercises parse_time and format_time from the “Function Exercises” section.
>>> from loops import sum_timestamps
>>> times = ["1:10", "0:12", "4:03", "2:45"]
>>> sum_timestamps(times)
'8:10'
>>> times = ["0:55", "0:55", "0:55", "0:55", "0:55"]
>>> sum_timestamps(times)
'4:35'
>>> sum_timestamps(["0:00"])
'0:00'
Here are the parse_time and format_time functions in case you need them:
def parse_time(time_string):
"""Return total seconds from string of minutes:seconds."""
sections = time_string.split(':')
return int(sections[0]) * 60 + int(sections[1])
def format_time(seconds):
"""Return a minutes:seconds string based on input seconds."""
sections = divmod(seconds, 60)
return f"{sections[0]}:{sections[1]:02d}"
Answers
def parse_time(time_string):
"""Return total seconds from string of minutes:seconds."""
sections = time_string.split(':')
return int(sections[0]) * 60 + int(sections[1])
def format_time(seconds):
"""Return a minutes:seconds string based on input seconds."""
sections = divmod(seconds, 60)
return f"{sections[0]}:{sections[1]:02d}"
def sum_timestamps(timestamps):
"""Return a timestamp that represents the sum of the input timestamps."""
total_time = 0
for time in timestamps:
total_time += parse_time(time)
return format_time(total_time)
Product
This is the product exercise in loops.py. Edit the loops.py file in the exercises directory to implement this exercise. To test it, run python test.py product in your exercises directory.
Write a product function that takes a list of numbers, multiplies the numbers together, and returns the result. Example:
>>> from loops import product
>>> product([5, 6, 8])
240
>>> product([10])
10
Note
Want to test this out manually (instead of using python test.py)?
You could could create a file called product_test.py with your own test code:
from loops import product
print("Calling product([5, 6, 8]")
print("Expected: 240")
print(" Actual:", repr(product([5, 6, 8])))
Then you can run that file to test your code:
$ python product_test.py
Answers
def product(numbers):
"""Returning all given numbers multiplied together."""
result = 1
for n in numbers:
result *= n
return result
Sum All
File: Edit the sum_all function in the loops.py file that is in the exercises directory.
Test: Run python test.py sum_all in your exercises directory.
Exercise: Make a function that accepts a list of lists of numbers and returns the sum of all of the numbers.
>>> from loops import sum_all
>>> matrix = [[1, 2, 3], [4, 5, 6]]
>>> sum_all(matrix)
21
>>> sum_all([[0, 1], [4, 2], [3, 1]])
11
Answers
With two loops:
def sum_all(number_lists):
"""Return the sum of all numbers in the given list-of-lists."""
total = 0
for numbers in number_lists:
for n in numbers:
total += n
return total
Using the sum function:
def sum_all(number_lists):
"""Return the sum of all numbers in the given list-of-lists."""
total = 0
for numbers in number_lists:
total += sum(numbers)
return total
Flatten a Matrix
This is the flatten exercise in loops.py.
File: Edit the flatten function in the loops.py file that is in the exercises directory.
Test: Run python test.py flatten in your exercises directory.
Exercise: Make a function that will take a matrix (a list of lists) and return a flattened version of the matrix.
>>> from loops import flatten
>>> matrix = [[row * 3 + incr for incr in range(1, 4)] for row in range(4)]
>>> matrix
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
>>> flatten(matrix)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Answers
Using for loops:
def flatten(matrix):
"""Return a flattened version of the given 2-D matrix (list-of-lists)."""
flattened = []
for item in matrix:
for element in item:
flattened.append(element)
return flattened
Using the list method extend:
def flatten(matrix):
"""Return a flattened version of the given 2-D matrix (list-of-lists)."""
flattened = []
for item in matrix:
flattened.extend(item)
return flattened
Matrix From String
File: Edit the matrix_from_string function in the loops.py file that is in the exercises directory.
Test: Run python test.py matrix_from_string in your exercises directory.
Exercise: Modify the matrix_from_string function so that it accepts a string and returns a list of lists of integers (found in the string).
Example:
>>> from loops import matrix_from_string
>>> matrix_from_string("1 2\n10 20")
[[1, 2], [10, 20]]
Answers
def matrix_from_string(string):
"""Convert rows of numbers to list of lists."""
matrix = []
for row in string.splitlines():
row_list = []
for x in row.split():
row_list.append(int(x))
matrix.append(row_list)
return matrix
X marks the spot
File: Edit the words_containing function in the loops.py file that is in the exercises directory.
Test: Run python test.py words_containing in your exercises directory.
Exercise: Modify the words_containing function so that it accepts a list of words and a letter and returns a list of all words in the string that contain the given letter. To test the function in the REPL, you can paste the function in from your text editor or import it as shown:
>>> from loops import words_containing
>>> words_containing(['My', 'life', 'is', 'my', 'message'], 'y')
['My', 'my']
>>> words_containing(['My', 'life', 'is', 'my', 'message'], 'i')
['life', 'is']
>>> words_containing(['My', 'life', 'is', 'my', 'message'], 'm')
['My', 'my', 'message']
Answers
def words_containing(words, letter):
"""Return all words that contain the given letter."""
matching_words = []
for word in words:
if letter in word.lower():
matching_words.append(word)
return matching_words
Consecutive Number Check
File: Edit the are_consecutive function in the loops.py file that is in the exercises directory.
Test: Run python test.py are_consecutive in your exercises directory.
Exercise: Modify the are_consecutive function so that it accepts a list of numbers and returns True if the numbers are consecutive, else return False. We aren’t going to worry about edge cases like an empty list or a list with only one number in it. To test the function in the REPL, you can paste the function in from your text editor or import it as shown:
>>> from loops import are_consecutive
>>> are_consecutive([3, 4, 5, 6])
True
>>> are_consecutive([3, 5, 9])
False
Answers
def are_consecutive(numbers):
"""Return True if numbers are consecutive, else return False."""
previous = None
for n in numbers:
if previous is None:
previous = n
else:
if n - previous != 1:
return False
previous = n
return True
Using slicing:
def are_consecutive(numbers):
"""Return True if numbers are consecutive, else return False."""
previous = numbers[0]
for n in numbers[1:]:
if n - previous != 1:
return False
previous = n
return True
Use short-circuiting of if-statement for more compact code:
def are_consecutive(numbers):
"""Return True if numbers are consecutive, else return False."""
previous = None
for n in numbers:
if previous is not None and n - previous != 1:
return False
previous = n
return True
Using slicing and a current counter variable (instead of previous):
def are_consecutive(numbers):
"""Return True if numbers are consecutive, else return False."""
current = numbers[0]
for n in numbers:
if n != current:
return False
current += 1
return True
File Exercises
Hint
If you get stuck for a minute or more, try searching Google or using help.
If you’re stuck for more than a few minutes, some of these links might be helpful for some of the exercises below:
Line Numbers
This is the line_numbers.py exercise in the modules directory. Create the file line_numbers.py in the modules sub-directory of the exercises directory. To test it, run python test.py line_numbers.py from your exercises directory.
Write a program that accepts a file as its only argument and prints out the lines in the files with a line number displayed in front of them.
Example:
If my_file.txt contains:
This file
is two lines long.
No wait, it's three lines long!
Running:
$ python line_numbers.py my_file.txt
Should print out:
1 This file
2 is two lines long.
3 No wait, it's three lines long!
Answers
import sys
filename = sys.argv[1]
lineno = 0
with open(filename) as my_file:
for line in my_file:
lineno += 1
print(lineno, line, end='')
Sort
This is the sort.py exercise in the modules directory. Create the file sort.py in the modules sub-directory of the exercises directory. To test it, run python test.py sort.py from your exercises directory.
Write a program sort.py which takes a file as input and sorts every line in the file (ASCIIbetically). The original file should be overwritten.
Example:
$ python sort.py names.txt
If file names.txt started out as:
John Licea
Freddy Colella
James Stell
Mary Carr
Doris Romito
Janet Allen
Suzanne Blevins
Chris Moczygemba
Shawn McCarty
Jennette Holt
It should end up as:
Chris Moczygemba
Doris Romito
Freddy Colella
James Stell
Janet Allen
Jennette Holt
John Licea
Mary Carr
Shawn McCarty
Suzanne Blevins
Answers
Reading, closing, and re-opening:
import sys
def main(filename):
with open(filename) as unsorted_file:
sorted_lines = sorted(unsorted_file)
with open(filename, mode='wt') as sorted_file:
for line in sorted_lines:
sorted_file.write(line)
if __name__ == "__main__":
main(sys.argv[1])
Reading and writing at once:
import sys
def main(filename):
with open(filename, mode='r+') as my_file:
sorted_lines = sorted(my_file)
my_file.seek(0)
for line in sorted_lines:
my_file.write(line)
if __name__ == "__main__":
main(sys.argv[1])
Find TODOs
This is the todos.py exercise in the modules directory. Create the file todos.py in the modules sub-directory of the exercises directory. To test it, run python test.py todos.py from your exercises directory.
Write a program that prints out every line in a file that contains the text TODO (I add TODO notes in my files to note to-dos I need to handle). Also print the line number before the line. The line numbers should be padded with zeros so that all the printed numbers are 3 digits long.
Example:
If workshop.rst contains:
This is how you make a list::
>>> numbers = [1, 2, 3]
.. TODO explain more about what lists are!
.. TODO add section on slicing
This is how you make a tuple::
>>> numbers = (1, 2, 3)
.. TODO explain more about tuples!
Running:
$ python todo.py workshop.rst
Should print out:
005 .. TODO explain more about what lists are!
007 .. TODO add section on slicing
013 .. TODO explain more about tuples!
Answers
import sys
filename = sys.argv[1]
with open(filename) as my_file:
for i, line in enumerate(my_file, start=1):
if 'TODO' in line:
print(f"{i:03d} {line}", end='')
New README
Create a program new_readme.py in the modules sub-directory of the exercises directory. To test it, run python test.py new_readme.py from your exercises directory.
Create a program that will prompt the user to enter a project name and then will create a readme.md file (in the current working directory) which includes that project name.
Here’s an example of running this program:
$ cd modules
$ python new_readme.py
Name: Skynet Lite
This should create a readme.md file that contains the project name anywhere within it:
# Skynet Lite
TODO
Make sure the file you create is called readme.md (all lowercase) as many file systems are case sensitive.
Hint
Use the input() function to prompt for user input and write to a file using the open() function with write mode.
Answers
project_name = input("Name: ")
with open("readme.md", "w") as readme_file:
readme_file.write(f"# {project_name}\n\nTODO\n")
Passphrase
This is the passphrase.py exercise in the modules directory.
Create the file passphrase.py in the modules sub-directory of the exercises directory.
To test it, run python test.py passphrase.py from your exercises directory.
Write a program passphrase.py that randomly generates 4-word passphrases.
When the program runs manually, you’ll need to specify a word list file which has one word on each line, like this one
Running this program with a word list file will result in 4 randomly-chosen words to be printed out:
$ python3 passphrase.py words.txt
lisa streets j rocket
$ python3 passphrase.py words.txt
salvador christians vacuum microwave
$ python3 passphrase.py words.txt
newfoundland pendant pan asus
Note
We will talk about Python’s random module in a future lesson, but here is a quick hint on how to use the random.choice function to get our random words for this exercise.
The choice function accepts a sequence (a list-like object) and returns a random item from that sequence:
>>> from random import choice
>>> colors = ["purple", "blue", "green", "orange"]
>>> choice(colors)
'purple'
>>> choice(colors)
'orange'
>>> choice(colors)
'orange'
You can take the floating point number and multiply it by the length of the word file, then make that an int, and use that for your index.
Answers
Here we’re using the string strip method to remove the newline character at the end of each line in our file and we’re using the random.choice function to randomly choose 4 words:
import random
import sys
filename = sys.argv[1]
words = []
with open(filename) as word_file:
for line in word_file:
words.append(line.strip())
generated_words = []
for i in range(4):
generated_words.append(random.choice(words))
print(" ".join(generated_words))
Here we’re using the string splitlines method to get the words, the random.choices function to randomly choose 4 words, and we’re passing separate arguments to print with * to put spaces between each word:
import random
import sys
filename = sys.argv[1]
with open(filename) as word_file:
words = word_file.read().splitlines()
print(*random.choices(words, k=4))
Count
This is the count.py exercise in the modules directory. Create the file count.py in the modules sub-directory of the exercises directory. To test it, run python test.py count.py from your exercises directory.
Write a program that accepts a file as an argument and outputs the number of lines, words, and characters in the file. In addition, it outputs the number of characters of the longest line in the file. Note the number of characters is for the whole file, which includes the newline character line endings. For the longest line, we do not want to include the line endings; only the actual number of characters of the line.
$ python count.py my_file.txt
Lines: 2
Words: 6
Characters: 28
Longest line: 17
Answers
import sys
MESSAGE_TEMPLATE = "\n".join((
"Lines: {line_count}",
"Words: {word_count}",
"Characters: {char_count}",
"Longest line: {max_line_length}",
))
def print_file_stats(filename):
with open(filename, mode='rt', encoding='utf-8') as stat_file:
contents = stat_file.read()
lines = contents.splitlines()
message = MESSAGE_TEMPLATE.format(
line_count=len(lines),
word_count=len(contents.split()),
char_count=len(contents),
max_line_length=max(len(x) for x in lines),
)
print(message)
if __name__ == "__main__":
print_file_stats(sys.argv[1])
Reverse
This is the reverse.py exercise in the modules directory. Create the file reverse.py in the modules sub-directory of the exercises directory. To test it, run python test.py reverse.py from your exercises directory.
Write a program that reverses a file character-by-character and outputs the newly reversed text into a new file.
Example:
If my_file.txt contains:
This file
is two lines long
Running:
$ python reverse.py my_file.txt elif_ym.txt
Should make elif_ym.txt contain:
gnol senil owt si
elif sihT
Hint: review some of the interesting ways that slice works.
Answers
import sys
def main(in_filename, out_filename):
with open(in_filename, mode='rt') as original_file:
contents = original_file.read()
with open(out_filename, mode='wt') as reversed_file:
reversed_file.write(contents[::-1])
if __name__ == "__main__":
main(*sys.argv[1:])
Concatenate
This is the concat.py exercise in the modules directory. Create the file concat.py in the modules sub-directory of the exercises directory. To test it, run python test.py concat.py from your exercises directory.
Write a program concat.py that takes any number of files as command-line arguments and sticks the files together, printing them to standard output.
If an error occurs while reading a file, the file should be skipped and an error should be printed.
Print the error messages to standard error (not standard output).
Tip
You can print to standard error like this:
>>> import sys
>>> print("this is an error", file=sys.stderr)
this is an error
Example usage of concat.py:
$ python concat.py file1.txt file2.txt file3.txt
This is file 1
[Errno 2] No such file or directory: 'file2.txt'
This is file 3
Answers
import sys
def main(args):
for filename in args:
try:
with open(filename) as concat:
contents = concat.read()
except Exception as error:
print(error, file=sys.stderr)
else:
print(contents, end='')
if __name__ == "__main__":
main(sys.argv[1:])