One-line summary

In this exercise, you create a train which is a double-linked list of cars, and count the length of the train and the number of seats available in the cars.

Exercise description

This exercise needs five classes: Train, TrainCar, Locomotive, Coach, and Cargo. TrainCar, Locomotive, Coach and Cargo are already complete.

A TrainCar is a type of car, which has a name. All other train car classes inherit from TrainCar. Locomotive is a TrainCar. Coach is a TrainCar, which has an attribute seats, which indicates how many seats this car has. Cargo is a TrainCar which has an attribute volume.

TrainCars are the nodes of a double-linked list, so they have attributes prevcar and nextcar to refer to the previous and next car, respectively. The prevcar of the first car is None, and the nextcar of the last car is also None.

Train is a train, which is implemented as a double-linked list. The nodes of the list are TrainCars. It has a reference head which indicates the first car, and a reference tail which indicates the last car. The add_car() method adds a TrainCar to the end of the list.

You have to fill in three methods of Train:

UML Diagram

Example

The following code:

t = Train()
t.add_car( Locomotive() )
t.add_car( Coach( 50 ) )
t.add_car( Coach( 60 ) )
t.add_car( Cargo( 150 ) )
t.add_car( Coach( 35, "First Class" ) )
t.add_car( Cargo( 50, "Fuel Car" ) )
print( t )
print( f"Length: {t.length()}" )
print( f"Total seats: {t.total_seats()}" )

should produce as output:

Locomotive
Coach: 50 seats
Coach: 60 seats
Cargo: 150 m3
First Class: 35 seats
Fuel Car: 50 m3
Length: 6
Total seats: 145

# Name:
# Student number:

class TrainCar:
    def __init__( self, name ):
        self.name = name
        self.prevcar = None
        self.nextcar = None
    def __repr__( self ):
        return f"{self.name}"

class Locomotive( TrainCar ):
    def __init__( self ):
        super().__init__( "Locomotive" )
    
class Coach( TrainCar ):
    def __init__( self, seats, name="" ):
        if name == "":
            name = "Coach"
        super().__init__( name )
        self.seats = seats
    def __repr__( self ):
        return super().__repr__() + f": {self.seats} seats"

class Cargo( TrainCar ):
    def __init__( self, volume, name="" ):
        if name == "":
            name = "Cargo"
        super().__init__( name )
        self.volume = volume
    def __repr__( self ):
        return super().__repr__() + f": {self.volume} m3"
        
class Train:
    def __init__( self ):
        self.head = None
        self.tail = None
    def add_car( self, car ):
        if self.head == None and not isinstance( car, Locomotive ):
            return False
        car.prevcar = self.tail
        self.tail = car
        penultnode = car.prevcar
        if penultnode != None:
            penultnode.nextcar = car
        else:
            self.head = car
        return True
    def length( self ):
        # Calculate and return the total length of the train.
        return 0
    def total_seats( self ):
        # Calculate and return the total number of seats on the train.
        return 0
    def __repr__( self ):
        # Fill in the __repr__() method in such a way that it consists of
        # all the TrainCars of which the Train consists, separated by
        # newlines.
        return ""
        
def main():
    t = Train()
    t.add_car( Locomotive() )
    t.add_car( Coach( 50 ) )
    t.add_car( Coach( 60 ) )
    t.add_car( Cargo( 150 ) )
    t.add_car( Coach( 35, "First Class" ) )
    t.add_car( Cargo( 50, "Fuel Car" ) )
    print( "---" )
    print( t )
    print( "---" )
    print( f"Length: {t.length()}" )
    print( f"Total seats: {t.total_seats()}" )
    
# ---
# Locomotive
# Coach: 50 seats
# Coach: 60 seats
# Cargo: 150 m3
# First Class: 35 seats
# Fuel Car: 50 m3
# ---
# Length: 6
# Total seats: 145    
    
if __name__ == "__main__":
    main()