I'm currently learning/working with classes in Python 3.10.2 as of writing this. What I am trying to achieve is to create a class instance which is an attribute within another class.
Here is some code I've been working on to help demonstrate my point.
class Vehicle():
"""To model a basic vehicle"""
def __init__(self, name, max_speed, millage):
"""Initiate class variables"""
self.name = name
self.max_speed = max_speed
self.millage = millage
self.tyre = Tyre()
def vehicle_details(self):
"""display vehicle details"""
return f"\nName: {self.name.title()}, Max speed: {self.max_speed}MPH, Millage: {self.millage} "
class Tyre():
"""A class specific for vehicle tyres"""
def __init__(self, size=16, pressure=36):
self.size = size
self.pressure = pressure
def tyre_details(self):
"""Print tyre details."""
print(f'Tyre size: {self.size} inches.')
print(f"Tyre pressure {self.pressure} PSI.")
In the Vehicle
class, I add Tyre()
as an attribute.
Now this code DOES work and I can call the Tyre
methods through my Vehicle
instances, but only when I allocate pre-determined values to size
and pressure
within the Tyre
class.
Is there either:
A way I can achieve this without having to allocate the pre-determined values within the Tyre
class?
This is the Traceback I receive if I do not allocate the pre-determined values:
TypeError: __init__() missing 2 required positional arguments: 'size' and 'pressure'
An easy way for me to overwrite the pre-determined values when calling this class and its methods if I am unable to remove the pre-determined values?
There are almost infinite ways to "solve" this, but I'll present a few.
None are "right", all involve tradeoffs and over time you'll learn which make sense where.
class Vehicle():
"""To model a basic vehicle"""
def __init__(self, name, max_speed, millage, tyre_size, tyre_pressure):
"""Initiate class variables"""
self.name = name
self.max_speed = max_speed
self.millage = millage
self.tyre = Tyre(tyre_size, tyre_pressure)
def vehicle_details(self):
"""display vehicle details"""
return f"\nName: {self.name.title()}, Max speed: {self.max_speed}MPH, Millage: {self.millage} "
class Tyre():
"""A class specific for vehicle tyres"""
def __init__(self, size, pressure):
self.size = size
self.pressure = pressure
def tyre_details(self):
"""Print tyre details."""
print(f'Tyre size: {self.size} inches.')
print(f"Tyre pressure {self.pressure} PSI.")
v1 = Vehicle("VehicleName", 120, 50_000, 16, 36)
# Or, with named arguments
v2 = Vehicle(
name = "VehicleName",
max_speed = 120,
millage = 50_000,
tyre_size = 16,
typre_pressure = 36
)
Here, you're just passing the Tyre
constructor's arguments through from the Vehicle
constructor to your construction of Tyre
. If every time you construct a Vehicle
you'll know the Tyre
specifications, this is fine. If not, you'll likely want an alternative.
Vehicle
with Tyre
instance as argumentclass Vehicle():
"""To model a basic vehicle"""
def __init__(self, name, max_speed, millage, tyre):
"""Initiate class variables"""
self.name = name
self.max_speed = max_speed
self.millage = millage
self.tyre = tyre
def vehicle_details(self):
"""display vehicle details"""
return f"\nName: {self.name.title()}, Max speed: {self.max_speed}MPH, Millage: {self.millage} "
class Tyre():
"""A class specific for vehicle tyres"""
def __init__(self, size, pressure):
self.size = size
self.pressure = pressure
def tyre_details(self):
"""Print tyre details."""
print(f'Tyre size: {self.size} inches.')
print(f"Tyre pressure {self.pressure} PSI.")
v = Vehicle(
name = "VehicleName",
max_speed = 120,
millage = 50_000,
tyre = Tyre(16, 36)
)
Similar to the previous option, but in the previous option each Vehicle
had their own Tyre
instance. Here you can "share" Tyre
objects. Probably not a huge deal in this case, but something to keep in mind, especially when you work with larger or more complex objects and/or where sharing becomes a benefit.
Sharing Tyre
instances might look like:
standard_tyre = Tyre(16, 36)
v1 = Vehicle(
name = "VehicleOne",
max_speed = 120,
millage = 50_000,
tyre = standard_tyre,
)
v2 = Vehicle(
name = "VehicleTwo",
max_speed = 140,
millage = 20_000,
tyre = standard_tyre,
)
class Vehicle():
"""To model a basic vehicle"""
def __init__(self, name, max_speed, millage):
"""Initiate class variables"""
self.name = name
self.max_speed = max_speed
self.millage = millage
self.tyre = None
def vehicle_details(self):
"""display vehicle details"""
return f"\nName: {self.name.title()}, Max speed: {self.max_speed}MPH, Millage: {self.millage} "
class Tyre():
"""A class specific for vehicle tyres"""
def __init__(self, size, pressure):
self.size = size
self.pressure = pressure
def tyre_details(self):
"""Print tyre details."""
print(f'Tyre size: {self.size} inches.')
print(f"Tyre pressure {self.pressure} PSI.")
v = Vehicle(
name = "VehicleName",
max_speed = 120,
millage = 50_000,
)
v.tyre = Tyre(16,36)
Maybe you don't know what the specifics of the Tyre
are when you're creating the Vehicle
, but you still want a Vehicle
instance.