Проверка дочерних / родительских отношений класса при предотвращении циклического импорта / циклической зависимости

У меня есть такие модули:

# module 1

from abc import ABC
from module3 import AnotherClass


class Parent(ABC):

    @classmethod
    def func1(cls):

        other_param = "foo"
        AnotherClass.some_func(cls, other_param)
# module 2

from module1 import Parent
from module3 import AnotherClass


class Child1(Parent):

    pass


class Child2(Parent):

    pass
# module 3

from module1 import Parent # only for checking if the object is a subclass of Parent


class AnotherClass(object):

    @classmethod
    def some_func(cls, child_cls, other_param):

        assert issubclass(child_cls, Parent)
        # do other stuff

теперь, если я сделаю это:

c1 = Child1()

c1.func1()

Я по праву получаю ImportError с жалобой на циклический импорт следующим образом:

ImportError: невозможно импортировать имя 'Parent' из частично инициализированного модуля 'module1' (скорее всего, из-за циклического импорта)

На самом деле AnotherClass не зависит от Parent или любого из дочерних классов, я импортирую Parent только для проверки, является ли переданный класс подклассом Parent в этой строке:

assert issubclass(child_cls, Parent)

Теперь я могу отложить импорт Parent следующим образом:

# module 3


class AnotherClass(object):

    @classmethod
    def some_func(cls, child_cls, other_param):

        from module1 import Parent # only for type checking
        assert issubclass(child_cls, Parent)
        # do other stuff

И это работает как шарм. Но по какой-то причине (мне неизвестной) мне неудобно делать это таким образом.

Теперь мой вопрос: могу ли я спокойно поспать, откладывая подобный импорт, или это укусит меня позже в будущем?

Если да, то как вы посоветуете мне выполнить эту проверку родства родитель / ребенок на AnotherClass.some_func()?

PS, прежде чем кто-либо поднимет этот вопрос, я знаю, что эта тема циклических зависимостей задавалась и отвечала много раз (например, this one), но я спрашиваю, возможно ли это конкретное решение и будет ли оно беспроблемным в долгосрочной перспективе.


person SercioSoydanov    schedule 23.05.2020    source источник


Ответы (2)


В вашем текущем решении нет ничего плохого. В этом случае вы также можете решить эту проблему следующим образом, что, по-моему, немного чище:

import module1


class AnotherClass(object):

    @classmethod
    def some_func(cls, child_cls, other_param):
        assert issubclass(child_cls, module1.Parent)
person ruohola    schedule 23.05.2020

Во-первых, вы на самом деле не выполняете там "проверку типов", asserts - это совершенно другое дело, поскольку они фактически обрабатываются во время выполнения.

Я думаю, вам лучше всего использовать настоящие подсказки типов, и, используя их, есть действительно простой и идиоматический способ избежать проблем с циклическим импортом:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from module1 import Parent


class AnotherClass:  # No need to inherit from `object` in Python 3.

    @classmethod
    def some_func(cls, child_cls: "Parent", other_param):
        ...
person ruohola    schedule 23.05.2020
comment
Вы правы, это не проверка типа. Я обновил вопрос соответственно. Другая проблема заключалась в том, что я явно не заявлял, что хочу проверять отношения родитель / потомок (подкласс) между двумя классами, а не его тип (это немного тонко) .Поэтому я не могу использовать это решение, потому что я действительно хочу сделать, это не проверка типов, как вы заявили. Я хочу проверить, является ли child_cls подклассом Parent - person SercioSoydanov; 23.05.2020