# 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))