Lab 9 Solutions
Solutions: You can find the file with solutions for all questions here.
OOP terminology
Object-oriented programming (OOP) is a style of programming that
allows you to think of code in terms of "objects." Here's an example of
a Car class:
class Car(object):
num_wheels = 4
def __init__(self, color):
self.wheels = Car.num_wheels
self.color = color
def drive(self):
if self.wheels <= Car.num_wheels:
return self.color + ' car cannot drive!'
return self.color + ' car goes vroom!'
def pop_tire(self):
if self.wheels > 0:
self.wheels -= 1
Here's some terminology:
- class: a blueprint for how to build a certain type of object.
The
Carclass (shown above) describes the behavior and data that allCarobjects have. instance: a particular occurrence of a class. In Python, we create instances of a class like this:
>>> my_car = Car('red')my_caris an instance of theCarclass.attribute or field: a variable that belongs to the class. Think of an attribute as a quality of the object: cars have wheels and color, so we have given our
Carclassself.wheelsandself.colorattributes. We can access attributes using dot notation:>>> my_car.color 'red' >>> my_car.wheels 4method: Methods are just like normal functions, except that they are tied to an instance or a class. Think of a method as a "verb" of the class: cars can drive and also pop their tires, so we have given our
Carclass the methodsdriveandpop_tire. We call methods using dot notation:>>> my_car = Car('red') >>> my_car.drive() 'red car goes vroom!'constructor: As with data abstraction, constructors describe how to build an instance of the class. Most classes have a constructor. In Python, the constructor of the class defined as
__init__. For example, here is theCarclass's constructor:def __init__(self, color): self.wheels = Car.num_wheels self.color = colorThe constructor takes in one argument,
color. As you can see, the constructor also creates theself.wheelsandself.colorattributes.self: in Python,selfis the first parameter for many methods (in this class, we will only use methods whose first parameter isself). When a method is called,selfis bound to an instance of the class. For example:>>> my_car = Car('red') >>> car.drive()Notice that the
drivemethod takes inselfas an argument, but it looks like we didn't pass one in! This is because the dot notation implicitly passes incarasselffor us.
Inheritance
Question 1: Cat
Here's the pet class. Your task is to create a specific pet:Cat
CURRENT_YEAR = 2018
class Animal(object):
def __init__(self):
self.is_alive = True # It's alive!!
class Pet(Animal):
def __init__(self, name, year_of_birth, owner=None):
Animal.__init__(self) # call the parent's constructor
self.name = name
self.age = CURRENT_YEAR - year_of_birth
self.owner = owner
def eat(self, thing):
self.talk()
if thing == "poison":
self.lose_life()
print(self.name + " ate a " + str(thing) + "!")
def talk(self):
print("..")
Implement a Cat class that inherits from Pet. Use superclass methods wherever possible.
class Cat(Pet):
"""
>>> my_cat = Cat("Furball", 2011, "Me", lives=2)
>>> my_cat.talk()
Meow!
>>> my_cat.name
'Furball'
>>> my_cat.lose_life()
>>> my_cat.is_alive
True
>>> my_cat.eat("poison")
Meow!
Furball ate a poison!
>>> my_cat.is_alive
False
>>> my_cat.lose_life()
'Cat is dead x_x'
"""
def __init__(self, name, year_of_birth, owner, lives=9):
assert type(lives) == int and lives > 0
"*** YOUR CODE HERE ***"
Pet.__init__(self, name, year_of_birth, owner)
self.lives = lives
def talk(self):
"""A cat says 'Meow!' when asked to talk."""
"*** YOUR CODE HERE ***"
print("Meow!")
def lose_life(self):
"""A cat can only lose a life if it has at least one
life. When there are zero lives left, the 'is_alive'
variable becomes False.
"""
"*** YOUR CODE HERE ***"
if not self.is_alive:
return "Cat is dead x_x"
self.lives -= 1
if self.lives == 0:
self.is_alive = False
Use OK to test your code:
python3 ok -q Cat --local
Table
In this lab, you will implement a version of the Table object from Data 8, that we are calling T88ble, appropriately pronounced T-88-bel. We aren't going to make you implement every single function, but we have table.select()ed the functions table.where(difficulty, are.below(insane)) and table.where(concepts, are.equal_to(relevant)).
Notice that we are representing tables here as a list of rows.
Question 2: Basic
Complete the num_rows, num_cols, and labels functions according to the docstrings.
def num_rows(self):
"""
Compute the number of rows in a table.
>>> simple_table = T88ble(simple_table_rows, simple_table_labels)
>>> simple_table.num_rows()
2
>>> longer_table = T88ble(longer_table_rows, longer_table_labels)
>>> longer_table.num_rows()
3
"""
"*** YOUR CODE HERE ***"
return ______
return len(self.rows)
def num_cols(self):
"""
Compute the number of cols in a table.
>>> simple_table = T88ble(simple_table_rows, simple_table_labels)
>>> simple_table.num_cols()
2
>>> longer_table = T88ble(longer_table_rows, longer_table_labels)
>>> longer_table.num_cols()
5
"""
"*** YOUR CODE HERE ***"
return ______
return len(self.rows[0])
def labels(self):
"""
Lists the column labels in a table.
>>> simple_table = T88ble(simple_table_rows, simple_table_labels)
>>> simple_table.labels()
['Flavor', 'Price']
>>> longer_table = T88ble(longer_table_rows, longer_table_labels)
>>> longer_table.labels()
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day']
"""
"*** YOUR CODE HERE ***"
return ______
return self.column_labels
Use OK to test your code:
python3 ok -q T88ble.num_rows --local
Use OK to test your code:
python3 ok -q T88ble.num_cols --local
Use OK to test your code:
python3 ok -q T88ble.labels --local
Question 3: Column Methods
Complete the column, select, and with_column functions according to the docstrings.
You might find the list.index(value) function to be particularly useful for this problem.
Check out the python docs here
def column(self, label):
"""
Returns the values of the column represented by label.
>>> simple_table = T88ble(simple_table_rows, simple_table_labels)
>>> simple_table.column("Flavor")
['chocolate', 'vanilla']
>>> longer_table = T88ble(longer_table_rows, longer_table_labels)
>>> longer_table.column("Eggs price")
[1.5, 2.5, 4]
>>> longer_table
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day']
[1990, 1, 1.5, 12, 7]
[2000, 2, 2.5, 25, 10]
[2010, 5, 4, 70, 36]
"""
"*** YOUR CODE HERE ***"
return ______
label_index = self.labels().index(label)
return [row[label_index] for row in self.rows]
def with_column(self, label, values):
"""
Returns a new table with an additional or replaced column.
label is a string for the name of a column, values is an list
>>> longer_table = T88ble(longer_table_rows, longer_table_labels)
>>> longer_table.with_column('Inflation rate', [i for i in range(longer_table.num_rows())])
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day', 'Inflation rate']
[1990, 1, 1.5, 12, 7, 0]
[2000, 2, 2.5, 25, 10, 1]
[2010, 5, 4, 70, 36, 2]
>>> longer_table
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day']
[1990, 1, 1.5, 12, 7]
[2000, 2, 2.5, 25, 10]
[2010, 5, 4, 70, 36]
"""
"*** YOUR CODE HERE ***"
return ______
new_rows = [self.rows[i][:] + [values[i]] for i in range(self.num_rows())]
new_labels = self.labels()[:] + [label]
return T88ble(new_rows, new_labels)
def select(self, labels):
"""
Create a copy of a table with only some of the columns,
reffered to by the list of labels.
>>> simple_table = T88ble(simple_table_rows, simple_table_labels)
>>> simple_table.select(["Flavor"])
['Flavor']
['chocolate']
['vanilla']
>>> simple_table
['Flavor', 'Price']
['chocolate', 2]
['vanilla', 1]
>>> longer_table = T88ble(longer_table_rows, longer_table_labels)
>>> longer_table.select(['Year', 'Average tank of gas'])
['Year', 'Average tank of gas']
[1990, 12]
[2000, 25]
[2010, 70]
>>> longer_table
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day']
[1990, 1, 1.5, 12, 7]
[2000, 2, 2.5, 25, 10]
[2010, 5, 4, 70, 36]
"""
"*** YOUR CODE HERE ***"
return ______
label_indices = [self.labels().index(label) for label in labels]
new_rows = [[row[i] for i in label_indices] for row in self.rows]
return T88ble(new_rows, labels)
Use OK to test your code:
python3 ok -q T88ble.column --local
Use OK to test your code:
python3 ok -q T88ble.with_column --local
Use OK to test your code:
python3 ok -q T88ble.select --local
Question 4: Sort
Complete the sort function according to the docstring.
You might find the sorted method to be very useful for this question. sorted can take in a key = lambda... just like the min function from the project.
Check out the python docs here
def sort(self, label, descending=True):
"""
Create a copy of a table sorted by the values in a column.
Defaults to ascending order unless descending = True is included.
>>> longer_table = T88ble(longer_table_rows, longer_table_labels)
>>> longer_table.sort("Year")
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day']
[2010, 5, 4, 70, 36]
[2000, 2, 2.5, 25, 10]
[1990, 1, 1.5, 12, 7]
>>> longer_table
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day']
[1990, 1, 1.5, 12, 7]
[2000, 2, 2.5, 25, 10]
[2010, 5, 4, 70, 36]
>>> simple_table = T88ble(simple_table_rows, simple_table_labels)
>>> simple_table.sort("Price", descending=False)
['Flavor', 'Price']
['vanilla', 1]
['chocolate', 2]
>>> simple_table
['Flavor', 'Price']
['chocolate', 2]
['vanilla', 1]
"""
"*** YOUR CODE HERE ***"
return ______
label_index = self.labels().index(label)
new_rows = sorted(self.rows[:], key=lambda x: x[label_index], reverse = descending)
return T88ble(new_rows, self.labels())
Use OK to test your code:
python3 ok -q T88ble.sort --local
Question 5: Where
Complete the where function according to the docstring.
def where(self, label, filter_fn):
"""
Create a copy of a table with only the rows that match a filter function.
>>> def above(x):
... return lambda y: y > x
...
>>> longer_table = T88ble(longer_table_rows, longer_table_labels)
>>> longer_table.where('Eggs price', above(2))
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day']
[2000, 2, 2.5, 25, 10]
[2010, 5, 4, 70, 36]
>>> longer_table
['Year', 'Bread price', 'Eggs price', 'Average tank of gas', 'Rent per day']
[1990, 1, 1.5, 12, 7]
[2000, 2, 2.5, 25, 10]
[2010, 5, 4, 70, 36]
"""
"*** YOUR CODE HERE ***"
return ______
label_index = self.labels().index(label)
new_rows = [row[:] for row in self.rows if filter_fn(row[label_index])]
return T88ble(new_rows, self.labels())
Use OK to test your code:
python3 ok -q T88ble.where --local