Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid circular dependency caused by type hinting of pointer attributes in python

Consider the two modules (in the same folder):

firstly, person.py

from typing import List

from .pet import Pet


class Person:
    def __init__(self, name: str):
        self.name = name
        self.pets: List[Pet] = []
    
    def adopt_a_pet(self, pet_name: str):
        self.pets.append(Pet(pet_name, self))

and then pet.py

from .person import Person

    
class Pet:
    def __init__(self, name: str, owner: Person):
        self.name = name
        self.owner = owner

the code above will not work, because of circular dependency. You'll get an error:

ImportError: cannot import name 'Person'

Some ways to make it work:

  1. keep the definition of the classes Person and Pet in the same file.
  2. do away with the pet.owner attribute (which is there as a convenient pointer)
  3. don't use type-hinting / annotation where it would cause circular references:

e.g. just have:

class Pet:
    def __init__(self, name: str, owner):

I see some drawback in all the options I've listed so far.

Is there another way? One that allows me to

  • split classes into different files
  • use type annotation in combined with pointers such as shown

Or: is there very good reason to instead follow one of the solutions I've already listed?

like image 580
levraininjaneer Avatar asked Oct 09 '17 07:10

levraininjaneer


People also ask

How do I get rid of cyclic dependency in python?

The easiest way to fix this is to move the path import to the end of the node module. Save this answer.

How can circular dependencies be avoided?

To reduce or eliminate circular dependencies, architects must implement loose component coupling and isolate failures. One approach is to use abstraction to break the dependency chain. To do this, you introduce an abstracted service interface that delivers underlying functionality without direct component coupling.

How do I fix the circular import error in python?

If the error occurs due to a circular dependency, it can be resolved by moving the imported classes to a third file and importing them from this file. If the error occurs due to a misspelled name, the name of the class in the Python file should be verified and corrected.


1 Answers

I ran into similar problems recently and solved it by using the following method:

import typing

if typing.TYPE_CHECKING:
    from .person import Person


class Pet:
    def __init__(self, name: str, owner: 'Person'):
        self.name = name
        self.owner = owner

There is a second solution described here, which requires Python >= 3.7 && < 3.10.

from __future__ import annotations  # <-- Additional import.
import typing

if typing.TYPE_CHECKING:
    from .person import Person


class Pet:
    def __init__(self, name: str, owner: Person):  # <-- No more quotes.
        self.name = name
        self.owner = owner

As of Python 3.10, the __future__ import will no longer be required.

like image 150
Gordon Avatar answered Oct 03 '22 22:10

Gordon