Jack
Jack

Reputation: 29

How do I structure this small project?

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

Answers (1)

berkeebal
berkeebal

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

Related Questions