Question: In Python: Background Classes allow us to define new types for Python. We can first think of a class as defining a new container instead
In Python:
Background
Classes allow us to define new types for Python. We can first think of a class as defining a new container instead of a list, tuple, set, or dictionary, we can have our own collection of values, each with a chosen name. We can then read out a value or update a value, much like reading or replacing the values in a list or dictionary. But we can also put methods in a class definition, giving us a way to specify the exact ways we should interact with values of this new type.
Once we have created a class definition, we can create as many objects of the new type as we want and use them in our programs. We can create entirely new types during the design phase of writing a program. This enables us to think in terms of types (and instances of types) that better model what we see in the real world, instead of endlessly relying on lists or dictionaries (and having to remember exactly how we intended to use those things as proxies for the values we actually had in mind).
Exceptions allow us to recover from unusual events some unavoidable (user types in bad file name or bad input in general), some unexpected (such as IndexErrors when stepping through a list). Without exceptions, we tend to program in an "ask permission, then attempt" style of calculation. But using exceptions, we can instead program in a "try it first, beg for forgiveness" approach by writing code that specifies how to recover from exceptions that we allowed to occur.
What's Allowed?
We've finally learned a substantial bit of programming! On this assignment, don't import anything except math; other than that you should be able to use any built-ins that you want; the project focuses on class structure, so there's not likely to be things that would make for any shortcuts.
Scenario
You will create world with cities, trains, and exciting journeys! This will require some proscribed class definitions. Create all the classes as defined below, all in your single .py file. Some of the classes build upon each other this is an example of aggregation. Many of the methods required below are very regular after you've seen one __init__ method on this program, you've sort of seen them all. While there might be a bit more typing than usual, the amount of mental work to "solve the puzzle" should be a bit minimal once you get used to the patterns.
Classes
You will be writing four classes (Train, City, Journey, and TrainCapacityException). Make sure you understand what these represent and how they relate to each other before continuing
Train class:
Instance variables:
name :: string # the name of the train
max_passengers :: int # the maximum number of passengers the train can transport
num_passengers :: int # the number of passengers currently on the train
speed_fps :: int # how fast the train travels (in feet per second)
Methods:
__init__(self, name, max_passengers, speed_fps) :: constructor, initializes all instance variables, num_passengers should default to 0 when a train is created with the constructor.
o Assumptions: speed_fps and max_passengers will always be a non-negative number; name will be a non-empty string 3
__str__(self) :: returns string representation with this format: "Train named Orient Express with 5 passengers will travel at 10.20mph"
o Notes: The quotes in the example are just our string boundaries! The first character in the above example is T; This method reports num_passengers (NOT max_passengers); Train speed is converted to mile per hour and always presented with 2 digits after the decimal point.
time_to_travel(self, distance_feet) :: Returns how long (in whole seconds) this train will take to travel distance_feet (distance in feet).
o Notes and Assumptions: This always returns an integer you could use integer division (//) or normal division followed by int() conversion to truncate the fractional part.; You can assume that distance_feet is always a non-negative integer.
load_passengers(self, num_people) :: Attempts to put num_people onto the train and update num_passengers. If the number of people supplied would not fit on the train, raise a TrainCapacityException and do not change the number of passengers currently on the train. [You may imagine this as the passengers get into a fight and no one is allowed on the train.]
o Note and Assumptions: You can assume that num_people is always a non-negative integer; When raising the exception, the number to raise is the number of people that cannot fit on the train (not the number of people that try to board); This function should return None. Remember: None is the default return if you dont return anything! You dont need to explicitly return None.
unload_passengers(self, num_people) :: Attempts to remove num_people from the train. If the number of people supplied is larger than the number of people on the train currently, raise a TrainCapacityException and do not change the number of passengers currently on the train. [You may imagine this as the passengers are so confused by the request that they just stay where they are looking confused.]
o Note and Assumptions: You can assume that num_people is always a non-negative integer; When raising the exception, the number to raise is the number of people that cannot unload, not the number of people that try to exit the train; This function should return None. Remember: None is the default return if you dont return anything! You dont need to explicitly return None.
TrainCapacityException class:
By specifying the parent class Exception as in the example below, objects (values) of this class can be raised and caught.
class TrainCapacityException(Exception):
Instance variables:
number :: int # how many people cant fit or be unloaded from the train
issue :: string # should be either "full" or "empty" # "full" indicates that the train was full # "empty" indicate the error was due to the train being empty 4
Methods:
__init__(self, number, issue="full") :: constructor; instantiates all instance variables. You can assume all incoming arguments are good and no need to perform any checking.
__str__(self) :: returns string representation matching this format (based on number and issue):
o '5 passengers cannot be loaded because the train is full!'
o '5 passengers cannot be unloaded because the train is empty!'
City class:
Instance variables:
name :: string # the name of the city
loc_x :: int # the citys x location (in an x-y-coordinate system) loc_y :: int # the citys y location (in an x-y-coordinate system)
stop_time :: int # how long (in number of seconds) trains stop in this city
Methods:
__init__(self, name, loc_x, loc_y, stop_time) :: constructor, initializes all instance variables. o Assumption: All incoming arguments are good; there is no need to perform any checking.
__str__(self) :: returns string representation with this format (note, the quotes in the example are just our quote boundaries! The first character in this example string is N): "New York (0,3). Exchange time: 5.00 minutes"
o Notes: The (0,3) in the example above are the coordinates of the city; Stop time is converted to number of minutes with 2 digits after the decimal point.
__eq__(self, other) :: Override the == method for City objects; returns True or False. Cities should be equal to each other if they are at the same location (loc_x and loc_y), regardless of their name or stop time. For example, City("New York", 0, 0, 0) and City("The Big Apple", 0, 0, 10) would be the same since they are physically located in the same location.
distance_to_city(self, city) :: Returns the distance between to cities (based on their x and y locations) as a float (no truncating). Remember: a2 + b2 = c2 !
Journey class: Instance variables: train :: Train # the train making the journey destinations :: list of City objects # the cities the train will visit, in order start_time :: int # the time the train will begin its journey # (in seconds since January 1st, 1970) Note: Why Thursday, Jan. 1st 1970? See: https://en.wikipedia.org/wiki/Unix_time 5
Methods:
__init__(self, train, destinations=None, start_time=0) :: constructor, initializes all instance variables. When no list of Cities is supplied, this creates an empty list as destinations. This should raise a TypeError if (a) train is not of type Train, (b) destinations is not of type list, or (c) destinations contains anything other than instances of City.
o Hint: Use the type() function to determine the type! (This was introduced near the beginning of the semester.)
__str__(self) :: returns string representation with this format (when printed out):
Journey with 3 stops: Philadelphia (0,0). Exchange time: 30.00 minutes New York (0,22). Exchange time: 10.00 minutes Boston (22,22). Exchange time: 5.00 minutes
Train Information: Train named Orient Express with 5 passengers will travel at 15.00mph
o Notes: Each city in the destinations is presented on a separate line, starting with a tab; The train information is the last line and ends with a newline
o Hints: You should reuse the string formatting for Train and City objects instead of defining them again!; Look at the sample run (and tester file) for more examples!
add_destination(self, city) :: Appends a city to the list of destinations and returns None. Assumption: You may assume city is an instance of City.
city_in_journey(self, city) :: Determines if a city is one of the destinations in the journey. Returns a boolean. Assumption: You may assume city is an instance of City.
check_journey_includes(self, start_city, dest_city) :: Check if the journey will include travel from the start_city to the dest_city. Stops are acceptable in between the two cities. Returns a boolean.
o Hints and Assumptions: There are many ways to solve this problem, but the lists index() method might be helpful here. You now know how to handle the exceptions it might raise! ; You may assume cities are instances of City.
total_journey_distance(self) :: Returns the total distance traveled (in feet as a float).
city_arrival_time(self, city) :: Returns the time at which the train will arrive at a given city. It returns an integer -- whole seconds since January 1st, 1970 (similar to how we specify start_time of the journey). Returns None if city is not included in journey. If the city is visited more than once on the journey, the first time the city is visited should be returned. You may assume city is an instance of City. The arrival time can be computed by:

city_departure_time(self, city) :: Returns the time at which the train will leave the given city. It returns an integer -- whole seconds since January 1st, 1970. Returns None if city is not included in journey. If the city is visited more than once on the journey, the last time the train leaves the city should be returned. You may assume city is an instance of City.
total_journey_time(self) :: Returns the total time taken on the journey (in seconds). This is the departure time of the last city minus the start time of the journey.
all_passengers_accommodated(self, unload_list, load_list) :: Given a list with the number of passengers unloading at each city, and a list of the number of passengers boarding the train at each city, determine if all passengers can be accommodated (i.e. they will all be able to board / leave as indicated in the lists). For example:
Unload List: [0,1,2], Load List: [1,2,0]
#at the first city, unload: 0 passengers will try to exit the train load: 1 passenger will try to board the train
#at the second city, unload: 1 passenger will try to exit the train load: 2 passengers will try to board the train
#at the third city, unload: 2 passengers will try to exit the train load: 0 passengers will try to board the train
o Notes: You must ask the train of the journey to load_passengers or unload_passengers. This will adjust the num_passengers on the train; Exceptions might be raised via the code from the Train class; you must handle these exceptions and return the required boolean as part of your exception handling; The train may or may not start with passengers already on the train at the first city; At the end, the number of people on the train may be non-zero.
o Assumptions: The length of unload_list and load_list will be the same as each other and both will be equal to the size of the destinations list for the journey; The unload_list and load_list will never contain invalid values (such as negative numbers), but they might contain 0s.

Stop Time at Journey Time Stop Time at Start City to Second City Second City Start City arrival Start City 2nd City 2nd City Arrival at City departure time arrival time departure time of Interest time (start time)
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
