I am learning about object oriented programming in Python using classes. I have attached an image of what I had in mind when structuring my classes. This is supposed script model cars. I want to know if the way that I set up my code it the ideal way of modeling cars.A few things:There are no syntax errors. I can create instances without errors.The subclasses uses *args to get the attributes of the parent class. Is this the correct way of doing that?I am using Python 2.7.5I have included a few example instances of the classEach class only has an __init__ method and a vehicle_print method. I have not yet added any other methods.class Vehicle(object):  # no instance of this class should be created    def __init__(self, typ, make, model, color, year, miles):        self.typ = typ        self.make = make        self.model = model        self.color = color.lower()        self.year = year        self.miles = miles    def vehicle_print(self):            print('Vehicle Type: ' + str(self.typ))            print('Make: ' + str(self.make))            print('Model: ' + str(self.model))            print('Color: ' + str(self.color))            print('Year: ' + str(self.year))            print('Miles driven: ' + str(self.miles))class GasVehicle(Vehicle):    def __init__(self, fuel_tank, *args):        self.fuel_tank = fuel_tank        Vehicle.__init__(self, *args)    def vehicle_print(self):        Vehicle.vehicle_print(self)        print('Fuel capacity (gallons): ' + str(self.fuel_tank))class ElectricVehicle(Vehicle):    def __init__(self, energy_storage, *args):        self.energy_storage = energy_storage        Vehicle.__init__(self, *args)    def vehicle_print(self):        Vehicle.vehicle_print(self)        print('Energy Storage (Kwh): ' + str(self.energy_storage))class HeavyVehicle(GasVehicle):  # no instance of this class should be created    def __init__(self, max_weight, wheels, length, *args):        self.max_weight = max_weight        self.wheels = wheels        self.length = length        GasVehicle.__init__(self, *args)    def vehicle_print(self):        GasVehicle.vehicle_print(self)        print('Maximum load (tons): ' + str(self.max_weight))        print('Wheels: ' + str(self.wheels))        print('Length (m): ' + str(self.length))class ConstructionTruck(HeavyVehicle):    def __init__(self, cargo, *args):        self.cargo = cargo        HeavyVehicle.__init__(self, *args)    def vehicle_print(self):        HeavyVehicle.vehicle_print(self)        print('Cargo: ' + str(self.cargo))class Bus(HeavyVehicle):    def __init__(self, seats, * args):        self.seats = seats        HeavyVehicle.__init__(self, *args)    def vehicle_print(self):        HeavyVehicle.vehicle_print(self)        print('Number of seats: ' + str(self.seats))class HighPerformance(GasVehicle):  # no instance of this class should be created    def __init__(self, hp, top_speed, *args):        self.hp = hp        self.top_speed = top_speed        GasVehicle.__init__(self, *args)    def vehicle_print(self):        GasVehicle.vehicle_print(self)        print('Horse power: ' + str(self.hp))        print('Top speed: ' + str(self.top_speed))class SportCar(HighPerformance):    def __init__(self, gear_box, drive_system, *args):        self.gearbox = gear_box        self.drive_system = drive_system        HighPerformance.__init__(self, *args)    def vehicle_print(self):        HighPerformance.vehicle_print(self)        print('Gear box: ' + self.gearbox)        print('Drive system: ' + self.drive_system)bmw = GasVehicle(30, 'SUV', 'BMW', 'X5', 'silver', 2003, 120300)  # regular carbmw.vehicle_print()printtesla = ElectricVehicle(85, 'Sport', 'Tesla', 'Model S', 'red', 2014, 1243)  # electric cartesla.vehicle_print()printlambo = SportCar('manual', 'rear wheel', 650, 160, 23, 'race car', 'Lamborgini', 'Enzo', 'dark silver', 2014, 3500)  # sportscarlambo.vehicle_print()printtruck = ConstructionTruck('cement', 4, 12, 21, 190, 'transport', 'Dirt Inc.', 'Dirt Blaster 100', 'blue', 1992, 120030)  # Construction trucktruck.vehicle_print()dpastepastebin
Model cars as classes
python;object oriented;classes;python 2.7
A couple of things to consider1) As a style thing, I'd prefer **kwargs to *args in your constructors. This makes things clearer since there's no chance that a change to a base class initializer will invalidate other code in subclasses.  Something like @MichaelUrman's example:class Vehicle(object):    Represent a vehicle.    def __init__(self, **kwargs):        self.info = kwargsAllows you to flexibly specify values while providing class level defaults that vary on application.  Super (as mentioned in other answers) is great for avoiding repetition here. For exampleclass FourByFour (Vehicle):    def __init__(self, **kwargs):        # provide a default transmissions        if not 'transmission' in kwargs: kwarg['transmission'] = 'Generic 4x4'        super (FourByFour, self).__init__(**kwargs)In the comments to  @MichaelUrman's example, OP asks how to create a specific class of car. A common python idiom (which is less common in other languages) is to create 'thin' subclasses that vary primarily by default content and then occasionally override default methods.  This is common in cases where you want a lot of classes which are mostly the same but differ in details and you don't expect to manage lots of subtle hierarchical changes.  From the 10,000 ft level this is the same using helper classes, since you're just populating a set of variables in a structured way; I prefer it to using helper functions because it allows you to add custom methods as necessary while keeping things data driven.For example:class Vehicle(object):   Represent a vehicle.    DEFAULTS = {'wheels':4, 'doors':2, 'fuel':'gasoline', 'passengers':2}    def __init__(self, **kwargs):       kwargs.update(self.DEFAULTS)       self.info = kwargs     def drive(self):       print Vroomclass Sedan(Vehicle):    DEFAULTS = {'wheels':4, 'doors':4, 'fuel':'gasoline', 'passengers':5}def Hybrid(Sedan):    DEFAULTS = {'wheels':4, 'doors':4, 'fuel':'smug', 'passengers':4}    def drive(self):        print purrrrrr...class Van(Vehicle):    DEFAULTS = {'wheels':4, 'doors':3, 'fuel':'gasoline', 'passengers':2, 'cargo':[]}    def unload(self):       if not self.cargo:          print no cargo loaded       else:          print unloading       return self.cargoConceptually this is halfway between 'traditional' inheritance heavy OOP and @MichaelUrman's data customization approach.  All three have their applications, and the neat thing about python is that it's very good at both @MichaelUrman's method and mine -- which is not true for languages obsessed with type-checking and signature maintenance.2) On the strategy level, in the long term you'll benefit from a finer grained approach.  Engines, transmissions, and tires for example might be their own classes instead of aspects of vehicle (a station wagon and a sedan, for example, might share everything but body styles).  The class hierarchy becomes less of an issue as you do more mix-and-match customization by creating collections of components. The usual pretentious programmer way to say this is 'prefer composition over inheritance'