Exceptions and Files Answers¶
Exception Exercises¶
Length or None¶
Write a function len_or_none that returns the length of a given object or None if the object has no length.
>>> len_or_none("hello")
5
>>> len(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
>>> len_or_none(4)
>>> print(len_or_none(4))
None
>>> len_or_none([])
0
>>> len_or_none(zip([1, 2], [3, 4]))
>>> print(len_or_none(zip([1, 2], [3, 4])))
None
>>> len_or_none(range(10))
10
Answers
def len_or_none(var):
try:
return len(var)
except TypeError:
return None
Average¶
Make a program average.py that calculates the average of all provided command-line arguments and prints an error message if no arguments are provided and second error message if invalid arguments are provided.
$ python average.py
No numbers to average!
$ python average.py 2 3 4 5 6 7
Average is 4.5
$ python average.py 2 3 4
Average is 3.0
$ python average.py 2 s 3
Invalid values entered, only numbers allowed!
Answers
import sys
def average(nums):
return sum(nums)/len(nums)
def main(arguments):
try:
numbers = [float(num) for num in arguments]
except ValueError:
raise SystemExit("Invalid values entered, only numbers allowed!")
try:
print("Average is {}".format(average(numbers)))
except ZeroDivisionError:
raise SystemExit("No numbers to average!")
if __name__ == "__main__":
main(sys.argv[1:])
Deep Add¶
Write a function deep_add that sums up all values given to it, including summing up the values of any contained collections.
>>> deep_add([1, 2, 3, 4])
10
>>> deep_add([(1, 2), [3, {4, 5}]])
15
Answers
With a counter variable:
def deep_add(iterable_or_number):
total = 0
try:
for x in iterable_or_number:
total += deep_add(x)
except TypeError:
total += iterable_or_number
return total
With sum:
def deep_add(iterable_or_number):
try:
numbers = (deep_add(x) for x in iterable_or_number)
except TypeError:
return iterable_or_number
else:
return sum(numbers)
Deep Flatten¶
Write a function deep_flatten that flattens all items given to it, including any contained collections, returning a list of all elements. As a bonus, make it work for strings, too.
>>> deep_flatten([1, 2, 3, 4])
[1, 2, 3, 4]
>>> deep_flatten([[1, 2], [3, [4, 5]]])
[1, 2, 3, 4, 5]
Bonus version should work like this:
>>> deep_flatten(["Hello ", "World"])
['Hello ', 'World']
>>> deep_flatten([(1, 2), [3, "bye", {4, 5}]])
[1, 2, 3, 'bye', 4, 5]
Answers
Naive version - works for the given inputs, but will not work for string input, because a single character will not generate the error we are depending upon:
def deep_flatten(iterable_or_item):
result = []
try:
for element in iterable_or_item:
result.extend(deep_flatten(element))
except TypeError:
result.append(iterable_or_item)
return result
Bonus version that doesn’t break for strings:
def deep_flatten(iterable_or_item):
result = []
if isinstance(iterable_or_item, (str, bytes)):
result.append(iterable_or_item)
else:
try:
for element in iterable_or_item:
result.extend(deep_flatten(element))
except TypeError:
result.append(iterable_or_item)
return result
Exit Twice¶
Write a program countdown.py that counts down from a given number, pausing for 1 seconds in between each number. The program should only exit if Ctrl-C is pressed twice.
You can pause for 1 second by using sleep from the time module.
Ctrl-C pressed just once:
$ python countdown.py 5
5
4
^C Press Ctrl-C again to exit
3
2
1
Ctrl-C pressed twice:
$ python countdown.py 10
10
9
^C Press Ctrl-C again to exit
8
7
^C Goodbye!
Answers
import sys
from time import sleep
def countdown(pause):
interrupted = False
for i in range(pause, 0, -1):
print(i)
try:
sleep(1)
except KeyboardInterrupt:
if interrupted:
print(" Goodbye!")
exit(0)
else:
interrupted = True
print(" Press Ctrl-C again to exit")
if __name__ == "__main__":
countdown(int(sys.argv[1]))
File Exercises¶
Count¶
Write a program that accepts a file as an argument and outputs the number of lines, words, and characters in the file.
$ python count.py my_file.txt
Lines: 2
Words: 6
Characters: 28
Longest Line: 17
Bonus: also output the maximum line length
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¶
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¶
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.
Bonus: 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)
if __name__ == "__main__":
main(sys.argv[1:])
Sort¶
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:
lines = unsorted_file.readlines()
with open(filename, mode='wt') as sorted_file:
sorted_file.writelines(sorted(lines))
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:
lines = my_file.readlines()
my_file.seek(0)
my_file.writelines(sorted(lines))
my_file.truncate()
if __name__ == "__main__":
main(sys.argv[1])