Handin these files to the Google Form:
library.py
memory.txt
https://goo.gl/forms/Fkr461qrsm27iZnP2
This assignment explores sharing of data as reflected in the contents of memory. For this part of the assignment we will work with dataclasses for (library) books and patrons (people who check out library books). Here are our data classes:
from dataclasses import dataclass
@dataclass
class Patron:
name: str
books_out: list
@dataclass
class Book:
title: str
author: str
checked_out: bool = False
borrower: Patron = None
The idea here is that when a patron checks out a book, the book is marked as checked out and the book is stored in the patron’s list of checked-out books. When a patron returns a book, the book is marked as checked in and removed from the patron’s list.
Note: In the Book
dataclass, we see a new feature: we can give fields default values that get set whenever you create a value of a dataclass. To create a Book
, we only provide the title and author data; the other two fields are created and set automatically. For example, here is a library catalog with four books (all with 1-word titles):
clean_catalog = [Book("Dracula", "Bram Stoker"),
Book("Cinder", "Marissa Meyer"),
Book("Cress", "Marissa Meyer"),
Book("Stardust", "Neil Gaiman")]
Write the following five programs in library.py
. You’ll need the usual imports:
from dataclasses import dataclass
from testlight import *
Addition: Use the function headers given below the questions, adding type annotations where necessary (this is to make sure you write functions with inputs in the correct order so we can test them).
find_books
, which takes a list of books and a search term and returns the list of books with the given term in the title or author fields.
checkout
, which takes a book and a patron and updates both to check out the book out to the patron. Nothing gets returned. Don’t check for potential errors, just update the fields to check out the book.
checkin
, which takes a book and a patron and updates both to indicate that the patron has returned the book. The borrower
field of the Book
should be set to None
. Nothing gets returned. Don’t check for potential errors, just update the fields to check in the book.
display_books
, which takes a list of books and prints out a summary of each book, each book on one line. This gives a more readable view of a list of books than just printing out the entire list. The summary for one book should look like:
Dracula by Bram Stoker [not available]
[not available] means the book is checked out. Print [available] for a book that is not checked out.
test_checkout
, which takes no inputs but runs tests to show that checkout
is working properly (we will not grade testing for your other functions but you should test them as needed to be confident that they work.)
In writing your tests, think back to the example from lecture where we wrote separate tests for individual fields. Develop your tests to check what should (and should not) have happened in each field after a checkout
.
Remember to add type annotations!
def find_books(books, query):
def checkout(book, patron):
def checkin(book, patron):
def display_books(books):
def test_checkout():
Here, we have provided three programs that use the functions you have just written. At the end of each program is a question marked with comment characters (#). Answer each of the questions. For questions that ask you to write a line of code, edit the programs to answer the questions (in library.py
). For questions that ask you to write out contents of memory, put your answers in a separate file named memory.txt
(Note: It doesn’t matter too much which file you put your answers in).
Refer to the notes from Wednesday’s lecture for the format you should use to write out the contents of memory.
Note: The top of each program shows how you can make a copy of an existing value for purposes of testing or experimentation. Here, we copy the clean_catalog
, to give us a local version for each program. You’ll need to add the following import to the top of your file to get the copy operation:
from copy import deepcopy
def program1():
"""Sample program #1"""
catalog = deepcopy(clean_catalog)
Josh = Patron("Josh", [])
checkout(catalog[0], Josh)
checkout(catalog[1], Josh)
# FILL in the blank so the following test passes
# (take the test out of comment when you are done)
# test("Josh books", Josh.books_out, ______)
def program2():
"""Sample program #2"""
catalog = deepcopy(clean_catalog)
checkout(catalog[0], Patron("Kathi", []))
checkout(catalog[2], Patron("Kathi", []))
# WRITE out the memory contents at this point
# You don't need to write out the
# original catalog in memory, just the copy.
#
# Either write a statement to print all the books Kathi
# has checked out, or write a comment explaining why
# this can't be done.
# __________________________
def program3():
"""Sample program #3"""
catalog = deepcopy(clean_catalog)
Julia = Patron("Julia", [])
Nam = Patron("Nam", [])
search = find_books(catalog, "Dracula")
checkout(search[0], Julia)
checkout(search[0], Nam)
print("Julia's books:")
display_books(Julia.books_out)
print("Nam's books")
display_books(Nam.books_out)
# WRITE out the memory contents at this point
# You don't need to write out the
# original catalog in memory, just the copy.
#
# In a few sentences, discuss whether the
# memory contents look as you'd expect
# for a program that properly checked books
# in and out. If not, what program error
# does this suggest?
You can build messages to print using a combination of the built-in function str
, which converts its argument to a string, and +
to concatenate strings. Here’s an example:
print("Today is " + str(date(2018, 7, 26)))
There are also more specialized ways to format strings, which, though not necessary for this course, can be extremely useful for everyday Python usage. Check out this link to learn about f-Strings
(don’t worry about the “class
” stuff).
This is very optional and only for people curious about how internals of programs work (you don’t need to know any of this for CS111, CS18, or programming in general).
This is how we’ll test your printing functions though
Say you have a function whose only consequence is printing something, and you want to test it, even though it neither returns something nor seems to effect a piece of data directly.
What’s actually happening when you run print
is that Python opens a ‘file’ located on your computer called “the standard output” (or ‘stdout’ for short). It writes the string you are printing into that file. That’s all Python does directly.
Whatever you are using to run Python (for example, PyCharm) regularly reads the “standard output” file and then renders it on the screen.
So if I run:
print('Hi')
Python will open the “stdout” ‘file’, and put “Hi” into it. Then some (very small) time later, Pycharm will read that same file, see that it contains “Hi”, then output “Hi” onto the console.
You can read more about standard output (and also standard input and standard error) here:
https://stackoverflow.com/questions/3385201/confused-about-stdin-stdout-and-stderr
You may notice they say stdout is not exactly a file, but there are negligible differences between what it really is (a “Stream”) and a file.
So how can you use this to test if a function prints something out? Check out this link:
https://stackoverflow.com/questions/1218933/can-i-redirect-the-stdout-in-python-into-some-sort-of-string-buffer