Reputation: 29
A Garage holds a list of Cars.
Each Car can only belong to one Garage.
Garages can add/remove cars.
Cars can move Garages.
Garages need to keep track of which Cars they have.
Cars need to store which Garage they are in.
I have three files:
car.py
import garage
class Car:
def __init__(self, garage: garage.Garage):
self.garage = garage
self.garage.add_car(self)
def print_garage(self):
print(f"This car's garage is {self.garage}")
def move_garage(self, to_garage: garage.Garage):
self.garage.remove_car(self)
self.garage = to_garage
self.garage.add_car(self)
garage.py
import car
class Garage:
def __init__(self):
self.car_list = []
def add_car(self, car: car.Car):
self.car_list.append(car)
def remove_car(self, car: car.Car):
self.car_list.remove(car)
sandbox.py
from car import Car
from garage import Garage
new_garage = Garage()
new_garage2 = Garage()
new_car = Car(
garage=new_garage
)
new_car.move_garage(
to_garage=new_garage2
)
In its current state I get this error
Exception has occurred: AttributeError
partially initialized module 'car' has no attribute 'Car' (most likely due to a circular import)
I've tried using a whole variety of 'import car', 'from car import Car', 'from car import *' on both classes, and have tried importing them differently from within sandbox.py.
I've tried having everything in the same file, but as both Car and Garage rely on each other, this does not fly.
I'm aware circular dependencies are normally a bad thing, but I haven't managed to find an alternative that is usable for this type of project design. It seems to come up in quite a few of the projects that I work on so i'm sure there's something I'm not seeing!
Edit (wasn't clear on the question/discussion): I'm looking for a way to structure a project like this to meet the above constraints. Especially the ones that let me use the Garage type inside Car, and the Car type inside Garage. I want to do this so I can (amoung other things) ensure that arguments are of the correct type, like this for example in Garage:
if not isinstance(car, car.Car): raise Exception()
Upvotes: 1
Views: 86
Reputation: 493
All the relations between your classes are architectual concers. But in your case you can check out forward references.
From pep docs
When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.
That being said, you should pass your class path as literal string e.g:
class Car:
def __init__(self, garage: 'garage.Garage'): # or 'Garage' if its in same file.
self.garage = garage
self.garage.add_car(self)
# other attrs and methods..
Upvotes: 1