CS 21, Spring 2017

Designing and Defining Classes

Complete the following exercises using paper and pencil:

  1. Imagine you are writing a class to represent information on a Swarthmore student. This Student class should keep track of the student's name, class year, and dormitory. It might have methods for 'getting' and 'setting' each of these pieces of data. Pick names and types for each of the instance variables you think this class would need. Write the __init__ method for this class. Write the __str__ method for this class so that it prints out information about a student like this:

    >>> student1 = Student("David", "Mauskop", "2020", "Parrish")
    >>> print(student1)
    D. Mauskop ('20) - Parrish
    
    class Student(obeject):
      
      def __init__(self, first_name, last_name, year, dorm):
        self.first = first_name
        self.last = last_name
        self.year = year
        self.dorm = dorm
        
      def __str__(self):
        template = "%s. %s ('%s) - %s"
        values = (self.first[0], self.last, self.year[2:], self.dorm)
        return template % values
        
      # getters and setters would go here...
  2. Think about how you'd define a class to represent dice (singular: 'Die'). If you wanted this Die class to support the constructor and methods below, what instance variable(s) would it need?

    Die(sides): constructor that takes the number of sides
    getNumSides(): returns the number of sides this die has
    roll(): simulates a roll of this die, returning a number chosen at random 
            from 1 to the number of sides
    rollNTimes(n): simulates n rolls of this die, returning a list of the results
    
    # Answer: just one instance variable for the number of sides.
    
    from random import *
    
    class Die(object):
    
      def __init__(self, sides):
        self.sides = sides
        
      def getNumSides(self):
        return self.sides
        
      def roll(self):
        return randrange(1, self.sides+1)
        
      def rollNTimes(self, n):
        rolls = []
        for i in range(n):
          nextRoll = self.roll()
          rolls.append(nextRoll)
        return rolls

    What if we a wanted to add a method, countOccurrences(k) that returned the number of times this die has produced a roll of k in its lifetime? What additional instance variable(s) would we need? Implement this method.

    # Answer: we could have a list where we keep track of occurrences
    
    from random import *
    
    class Die(object):
    
      def __init__(self, sides):
        self.sides = sides
        self.occurrences = [0]*sides
        
      def roll(self):
        nextRoll = randrange(1, self.sides+1)
        self.occurrences[nextRoll-1] += 1
        return nextRoll
        
      def countOccurrences(self, k):
        return self.occurrences[k-1]
        
      # getNumSides and rollNTimes would be unchanged

    Write code that creates a twenty sided die, rolls it 100 times, and prints out the number of 15s that occurred in those 100 rolls.

    die = Die(20)
    die.rollNTimes(100)
    print("15 came up %d times." % die.countOccurrences(15))

    What instance variable(s) would be required to support a getHistory() method that returns a list of all the rolls that have happened in this die's lifetime?

    # Answer: you'd need a list to store the entire history of rolls
    
    from random import *
    
    class Die(object):
    
      def __init__(self, sides):
        self.sides = sides
        self.occurrences = [0]*sides
        self.history = []
        
      def roll(self):
        nextRoll = randrange(1, self.sides+1)
        self.occurrences[nextRoll-1] += 1
        self.history.append(nextRoll)
        return nextRoll
        
      def getHistory(self):
        return self.history
        
      # getNumSides, rollNTimes, and countOccurrences would be unchanged
  3. Write the __init__ method for a Book class with instance variables to keep track of a book's title, author, price, genre, page count, and reviews. When the Book object is constructed, there will be no reviews--these will be added later through the addReview(review) method. Show how you might implement this method. Write methods called getPrice() and setPrice(newPrice) that let you access and change the price of the book.

    class Book(object):
    
      def __init__(self, title, author, price, genre, pages):
        self.title = title
        self.author = author
        self.price = price
        self.genre = genre
        self.pages = pages
        self.reviews = []
        
      def addReview(self, review):
        self.reviews.append(review)
        
      def getPrice(self):
        return self.price
        
      def setPrice(self, newPrice):
        self.price = newPrice
  4. Design a ShoppingCart class that might be used by an online store. Objects of this class should know: the customer's name, the customer's address, the items in the customer's shopping cart, and the prices of these items. Describe the names and types for the instance variables of this class. Write the __init__ method for this class and then write an addItem(itemName, itemPrice) method that adds an item to the list of items and its price to the list of prices.

    class ShoppingCart(object):
    
      def __init__(self, name, address):
        self.name = name
        self.address = address
        self.items = []
        self.prices = []
        
      def addItem(self, itemName, itemPrice):
        self.items.append(itemName)
        self.prices.append(itemPrice)
        
      # Would also want more getters and setters
  5. Design a NetflixAccount class that keeps track of the username and movie reviews for a Netflix user. Rather than have separate instance variables to represent the list of movies a user has watched and the list of their corresponding reviews (from 1 to 5), keep one list of MovieReview objects. The MovieReview(name, rating) constructor takes the name of a movie as a string and the rating as an int from 1 to 5. Assume the MovieReview class has already been implemented. Write the __init__ method and an addReview(movie, rating) method for the NetflixAccount class.

    from moviereview import *
    
    class NetflixAccount(object):
      
      def __init__(self, username):
        self.username = username
        self.reviews = []
        
      def addReview(self, movie, rating):
        newReview = MovieReview(movie, rating)
        self.reviews.append(newReview)
        
      # We would also want more getters and setters

    How could we refine our ShoppingCart class so that, like the NetflixAccount class, it only needs one list, instead of separate lists for the items and item prices?

    class CartItem(object):
      def __init__(self, name, price):
        self.name = name
        self.price = price
        
      def getName(self):
        return self.name
        
      def getPrice(self):
        return self.price
        
      def setName(self, newName):
        self.name = newName
        
      def setPrice(self, newPrice):
        self.price = newPrice
        
    class ShoppingCart(object):
    
      def __init__(self, name, address):
        self.name = name
        self.address = address
        self.items = []
        
      def addItem(self, itemName, itemPrice):
        newItem = CartItem(itemName, itemPrice)
        self.items.append(newItem)
        
      # Would also want more getters and setters
  6. Write a Burger class that knows whether it is a veggie burger or a hamburger, what toppings it has, and how many bites it has left. Assume a burger initially has 10 bites. Here's a description of the interface for this class:

    Burger(isVeggie): constructor for Burger class. If isVeggie is 
                      True, that means it's a veggie burger.
    addTopping(toppingName): adds a topping to the burger
    takeBite(): take one of the 10 bites out of the burger, assuming 
                there is at least one bite left.
    

    Write a __str__ method for this class so that printing the burger looks like:

    >>> burger1 = Burger(False)
    >>> burger1.addTopping("lettuce")
    >>> burger1.addTopping("tomato")
    >>> burger1.addTopping("cheddar cheese")
    >>> burger1.takeBite()
    "Yummm. You have 9 bites left."
    >>> print(burger1)
    Hamburger with lettuce, tomato, cheddar cheese.
    9 bites left.
    >>> burger2 = Burger(True)
    >>> print(burger2)
    Veggie burger with no toppings.
    10 bites left.
    

    If the takeBite() method is called on a burger with no bites left, it should print something like, 'Sorry, you already finished this burger.'

    class Burger(object):
    
      def __init__(self, isVeggie):
        self.veggie = isVeggie
        self.bites = 10
        self.toppings = []
        
      def __str__(self):
        if self.veggie:
          burgerType = "Veggie burger"
        else:
          burgerType = "Hamburger"
    
        if len(self.toppings) == 0:
          toppingsList = "no toppings"
        else:
          toppingsList = ", ".join(self.toppings)
          
        if self.bites > 0:
          bitesMessage = "%d bites left." % self.bites
        else:
          bitesMessage = "No bites left."
          
        return "%s with %s.\n%s" % (burgerType, toppingsList, bitesMessage)
          
      def addTopping(self, toppingName):
        self.toppings.append(toppingName)
        
      def takeBite(self):
        if self.bites == 0:
          print("Sorry, you already finished this burger.")
        else:
          self.bites -= 1
          print("Yummm. You have %d bites left." % self.bites)