Модуль циклического импорта

В моем проекте я использую модуль interaction который содержит несколько "интерфейсов" для доступа к системным функциям. Например, у меня есть 2 модуля для реализации различных интерфейсов, и один из них зависит от другого интерфейса.

module1.py:

value = 5

def init():
    pass

def provide_int():
    global value
    return value

module2.py:

def init():
    import interaction
    global value
    value = str(interaction.int_provider.provide_int())

def provide_string():
    global value
    return value

Поэтому я хочу использовать это так же:

Interaction.py:

from importlib import import_module

globals()['int_provider'] = import_module('module1')
globals()['int_provider'].init()
globals()['str_provider'] = import_module('module2')
globals()['str_provider'].init()

Таким образом, во время инициализации module2 (вызывая функцию init) Я получаю ImportError из-за циклического импорта. Этот пример, конечно, синтетический, но общее состояние одно и то же.

У меня есть 2 вопроса:
1. Это правильный способ использовать "глобальный" модуль, как мой interaction.py проксирование интерфейсов?
2. Как я могу победить этот циклический импорт?

2 ответа

Я предлагаю покончить с init функции и делает provide_string() Функция устанавливает свои значения при первом вызове. Поскольку во время загрузки модуля происходит очень мало, не будет проблем с модулями, ссылающимися друг на друга. Кроме того, вы можете упростить interaction модуль с помощью as ключевое слово с регулярным импортом:

iteraction.py:

 import module1 as int_provider
 import module2 as string_provider

module1.py:

 value = 5

 def provide_int():
     return value # no global statement necessary, since we don't assign to value

module2.py:

 value = None # this will be set up after the first call to provide_string

 def provide_string():
     global value # global is needed, since we may assign a new string to value
     if value is None:
         import interaction
         value = str(interaction.int_provider.provide_int())
     return value

Как я могу победить этот циклический импорт?

Вы можете использовать лениво созданные провайдеры, используя что-то вроде django.utils.functional.SimpleLazyObject. Таким образом, вы инициализируете провайдера в момент обращения к любому его атрибуту. Вы должны будете помнить, чтобы запомнить результат в SimpleLazyObjectтак как переданная функция будетпереоцениваться каждый раз.

Использование:

def init_str_provider():
    if not hasattr(init_str_provider, 'provider'):
        init_str_provider.provider = import_module('module2')
        init_str_provider.provider.init()
    return init_str_provider.provider

str_provider = SimpleLazyObject(init_str_provider)

Это правильный способ использовать "глобальный" модуль, как мойinteraction.pyпроксирование интерфейсов?

Если бы я должен был создать такую ​​структуру с интерфейсами, я бы предпочел пойти с:

  • Метод регистрации класса провайдера в interfaces.py,
  • Использование классов для представления поставщика (упрощение взаимодействия благодаря наследованию!).
  • Обеспечить get_provider(name) функция в interfaces.py разрешить ленивую инициализацию провайдера.

Такое разделение облегчает тестирование и макет отдельных провайдеров и оставляет больше места для настройки. Кроме того, если вам когда-либо понадобится расширить поведение одного провайдера внутри другого, объекты класса сохраняют свое состояние внутри себя, а не на уровне модуля. Таким образом, вы на самом деле можете наследовать от других поставщиков, не беспокоясь о том, что любой дочерний поставщик будет работать с внутренними компонентами автономного базового поставщика.

Другие вопросы по тегам