269 lines
8.2 KiB
Python
269 lines
8.2 KiB
Python
# The following is a template of the design.
|
|
# you will need to complete the definition of each class, including class attributes,
|
|
# instance attributes, and instance methods. However, please do not change the class
|
|
# names provided below.
|
|
|
|
# IMPORTANT: In order to get your program pass the test, make sure your class constructors
|
|
# use exactly the same parameter names as in the test examples provided at the end of this template.
|
|
|
|
# ================Your codes start here=============
|
|
# Notes:
|
|
# - Should a motorboat (or eboat) really *be* an engine?
|
|
# - Why are the keyword arguments abbreviated?
|
|
|
|
|
|
def class_bases(cls):
|
|
"""Generator of the base classes of a class (including the given class).
|
|
|
|
Depth-first, post-order, may yield duplicates
|
|
"""
|
|
for cls_ in cls.__bases__:
|
|
yield from class_bases(cls_)
|
|
yield cls
|
|
|
|
|
|
def field_repr(obj) -> str:
|
|
"""String representation of an object
|
|
|
|
Lists fields with type annotations in object's class and superclasses"""
|
|
classes = class_bases(obj.__class__)
|
|
attrs = (
|
|
attr for cls in classes for attr in getattr(cls, "__annotations__", {}).keys()
|
|
)
|
|
attrs_vals = ",".join(f"{attr}={getattr(obj, attr, None)!r}" for attr in attrs)
|
|
return f"{obj.__class__.__name__}({attrs_vals})"
|
|
|
|
|
|
class Boat:
|
|
"""A boat
|
|
|
|
Attributes:
|
|
- `color`: color of boat
|
|
- `brand`: boat brand
|
|
- `year`: manufacture year
|
|
|
|
Class Attributes:
|
|
- `all_boats`: a list of all boats ever constructed
|
|
"""
|
|
|
|
all_boats = []
|
|
|
|
color: str
|
|
brand: str
|
|
year: int
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
co: str,
|
|
br: str,
|
|
yr: int,
|
|
**kwargs,
|
|
):
|
|
"""Initialize a `Boat`.
|
|
|
|
Takes keyword-only arguments `co` (color), `br` (brand), and `yr` (year)
|
|
as described in class docstring
|
|
"""
|
|
Boat.all_boats.append(self)
|
|
super().__init__(**kwargs)
|
|
|
|
self.color = co
|
|
self.brand = br
|
|
self.year = yr
|
|
|
|
def __str__(self):
|
|
return field_repr(self)
|
|
|
|
def get_boat_age(self, current_year: int) -> int:
|
|
"""Get the age of this boat given the current year"""
|
|
return current_year - self.year
|
|
|
|
|
|
class Engine:
|
|
"""An Engine
|
|
|
|
Attributes:
|
|
- `tech`: Technology used by the engine.
|
|
Either 'gas' or 'electric'
|
|
- `engine_speed`: maximum speed in miles per hour
|
|
determined by `tech` - gas engines have speed 80mph, electric 20mph
|
|
|
|
Class Attributes:
|
|
- `speeds`: mapping of engine tech to speed
|
|
"""
|
|
|
|
speeds = {"gas": 80, "electric": 20}
|
|
|
|
engine_speed: float
|
|
tech: str
|
|
|
|
def __init__(self, *, tech: str, **kwargs):
|
|
"""Initialize an `Engine`
|
|
|
|
Takes keyword-only argument `tech` (see class docstring)
|
|
"""
|
|
super().__init__(**kwargs)
|
|
|
|
self.tech = tech
|
|
self.engine_speed = Engine.speeds[tech]
|
|
|
|
def __str__(self):
|
|
return field_repr(self)
|
|
|
|
def get_engine_speed(self) -> float:
|
|
"""Get the engine speed for this engine in miles per hour"""
|
|
return self.engine_speed
|
|
|
|
|
|
class Motorboat(Boat, Engine):
|
|
"""A Motorboat
|
|
|
|
Always uses 'gas' engine tech
|
|
Attributes:
|
|
- `fuel_level`: remaining level of fuel in gallons
|
|
- `fuel_efficiency`: range with a certain amount of fuel in miles per gallon
|
|
"""
|
|
|
|
fuel_level: float
|
|
fuel_efficiency: float
|
|
|
|
def __init__(self, *, fl: float, fe: float, **kwargs):
|
|
"""Initialize a `Motorboat`
|
|
|
|
Takes keyword-only arguments `fl` (fuel level) and `fe` (fuel efficiency)
|
|
as described in class docstring, in addition to the arguments described in `Boat`
|
|
"""
|
|
super().__init__(**kwargs, tech="gas")
|
|
|
|
self.fuel_level = fl
|
|
self.fuel_efficiency = fe
|
|
|
|
def get_max_speed(self) -> float:
|
|
"""Get the max speed of the motorboat"""
|
|
return self.engine_speed
|
|
|
|
def cal_travel_time(self, distance: float) -> float:
|
|
"""Calculate the time to travel `distance` miles
|
|
|
|
Prints a message and returns max engine runtime
|
|
if the engine would run out of fuel before the destination
|
|
"""
|
|
range = self.fuel_level * self.fuel_efficiency
|
|
if range < distance:
|
|
print(
|
|
f"This motorboat runs out of fuel {distance - range} miles away from the destination."
|
|
)
|
|
return min(range, distance) / self.engine_speed
|
|
|
|
|
|
class Pedalboat(Boat):
|
|
"""A Pedalboat
|
|
|
|
Attributes:
|
|
- `pedal_speed`: speed attainable by pedalling in miles per hour
|
|
Generally in range [10, 20]
|
|
"""
|
|
|
|
pedal_speed: float
|
|
|
|
def __init__(self, *, ps: float, **kwargs):
|
|
"""Initialize a `Pedalboat`
|
|
|
|
Clamps `pedal_speed` to `10 <= pedal_speed <= 20`
|
|
|
|
Takes keyword-only argument `ps` (pedal speed), as described in the class docstring,
|
|
in addition to the arguments described in `Boat`
|
|
"""
|
|
super().__init__(**kwargs)
|
|
|
|
self.pedal_speed = ps
|
|
self.check_speed()
|
|
|
|
def get_pedal_speed(self) -> float:
|
|
"""Get the pedalling speed of the boat in miles per hour"""
|
|
return self.pedal_speed
|
|
|
|
def cal_travel_time(self, distance: float) -> float:
|
|
"""Calculate the time to travel `distance` miles
|
|
"""
|
|
return distance / self.pedal_speed
|
|
|
|
def check_speed(self):
|
|
"""Clamps `pedal_speed` to `10 <= pedal_speed <= 20`
|
|
Returns whether the speed was already in this range
|
|
"""
|
|
if 10 <= self.pedal_speed <= 20:
|
|
return True
|
|
self.pedal_speed = min(20, max(self.pedal_speed, 10))
|
|
return False
|
|
|
|
|
|
class Eboat(Pedalboat, Engine):
|
|
"""An `Eboat`
|
|
|
|
Always has engine tech 'electric'
|
|
Attributes:
|
|
- `battery_time`: remaining battery running time in hours
|
|
"""
|
|
|
|
battery_time: float
|
|
|
|
def __init__(self, *, bt: float, **kwargs):
|
|
"""Initialize an `Eboat`
|
|
|
|
Takes keyword-only argument `bt` (battery time), as described in the class docstring,
|
|
in addition to the arguments described in `Pedalboat`
|
|
"""
|
|
super().__init__(**kwargs, tech="electric")
|
|
|
|
self.battery_time = bt
|
|
|
|
def get_max_speed(self) -> float:
|
|
"""The maximum speed attainable in this boat by running the engine and pedalling"""
|
|
return self.pedal_speed + self.engine_speed
|
|
|
|
def cal_travel_time(self, distance: float) -> float:
|
|
"""Calculate the time to travel `distance` miles
|
|
"""
|
|
battery_range = self.get_max_speed() * self.battery_time
|
|
return (
|
|
min(distance, battery_range) / self.get_max_speed()
|
|
+ max(distance - battery_range, 0) / self.pedal_speed
|
|
)
|
|
|
|
|
|
# ============ End of your codes here ==================
|
|
|
|
|
|
# ============No modification beyond here =============
|
|
# the following is a list of test instances, please do not modify them
|
|
if __name__ == "__main__":
|
|
# arguments: co - color, br - brand, yr - year, tech - technology used in engine
|
|
boat1 = Boat(co="Black", br="Trek", yr=2012)
|
|
engine1 = Engine(tech="gas")
|
|
print(engine1.get_engine_speed())
|
|
|
|
# arguments: co - color, br - brand, yr - year, ps - pedal speed
|
|
pedalboat1 = Pedalboat(co="Red", br="GIANT", yr=2015, ps=15)
|
|
pedalboat2 = Pedalboat(co="Red", br="GIANT", yr=2015, ps=30)
|
|
print(pedalboat1.get_pedal_speed())
|
|
print(pedalboat2.get_pedal_speed())
|
|
print(pedalboat1.cal_travel_time(300))
|
|
|
|
# arguments: co - color, br - brand, yr - year, ps - pedal speed, bt - battery time
|
|
eboat1 = Eboat(co="Blue", br="Basis", yr=2018, ps=15, bt=10)
|
|
print(eboat1.get_max_speed())
|
|
print(eboat1.cal_travel_time(350))
|
|
print(eboat1.cal_travel_time(650))
|
|
|
|
# arguments: co - color, br - brand, yr - year, fl - fuel level, fe - fuel efficiency
|
|
motorboat1 = Motorboat(co="Silver", br="YAMAHA", yr=2013, fl=40, fe=12)
|
|
print(motorboat1.get_max_speed())
|
|
print(motorboat1.cal_travel_time(300))
|
|
print(motorboat1.cal_travel_time(600))
|
|
|
|
# get the age of all bikes created
|
|
for b in Boat.all_boats:
|
|
print(b.get_boat_age(2023))
|