Как правильно обрабатывать циклическую зависимость модуля в Python?

попытка найти хороший и правильный шаблон для обработки круговой зависимости модуля в Python. Обычно решение состоит в том, чтобы удалить его (через рефакторинг); однако в этом конкретном случае мы действительно хотели бы иметь функциональность, требующую кругового импорта.

редактировать: согласно ответам ниже, обычным углом атаки для такого рода проблем будет рефакторинг. Однако, ради этого вопроса, предположим, что это не вариант (для любая причина.)

проблема:

The logging модуль требует configuration модуль для данных конфигурации. Однако для некоторых из configuration функции я бы очень хотел использовать пользовательские функции ведения журнала, которые определены в logging модуль. Очевидно, что импорт logging модуль configuration выдает ошибку.

возможные решения мы можем думать из:

  1. не надо. Как я уже говорил, это не лучший вариант, если только все остальные возможности не уродливы и плохи.

  2. Monkey-исправьте модуль. Это звучит не так уж плохо: нагрузка logging модуль динамически в configuration после начальный импорт, и перед тем, как любая из его функций фактически используются. Это подразумевает определение глобальных переменных для каждого модуля, хотя.

  3. инъекции зависимостей. Я прочитал и столкнулся с альтернативами инъекций зависимостей (особенно в корпоративном пространстве Java), и они удаляют часть этой головной боли; однако они могут быть слишком сложными для использования и управления, чего мы хотели бы избежать. Я не знаю, как панорама, хотя об этом в Python.

каков хороший способ включить эту функциональность?

спасибо очень!

4 ответов


Как уже было сказано, вероятно, требуется рефакторинг. Согласно именам, это может быть нормально, если модули ведения журнала используют конфигурацию, когда думаешь о том, что должно быть в конфигурации, думаешь о параметрах конфигурации, тогда возникает вопрос, почему это ведение журнала конфигурации вообще?

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

без внутреннего знания и с использованием только здравого смысла модуль "конфигурация" должен быть чем-то простым без большой обработки, и он должен быть листом в дереве импорта.

надеюсь, что это помогает!


это будет работать для вас?

# MODULE a (file a.py)
import b
HELLO = "Hello"

# MODULE b (file b.py)
try:
    import a
    # All the code for b goes here, for example:
    print("b done",a.HELLO))
except:
    if hasattr(a,'HELLO'):
        raise
    else:
        pass

теперь я могу сделать импорт b. Когда циклический импорт (вызванный инструкцией import b в A) создает исключение, оно перехватывается и отбрасывается. Конечно, весь ваш модуль b должен будет отступить на один дополнительный интервал блока, и вы должны иметь внутреннее знание того, где переменная HELLO объявлена в a.

Если вы не хотите изменять b.py вставив try: except: logic, вы можете переместить весь источник b в новый файл, назовем его c.py, и сделать простой файл b.py вот так:

# new Module b.py
try:
    from c import *
    print("b done",a.HELLO) 
except:
    if hasattr(a,"HELLO"):
        raise
    else:
        pass

# The c.py file is now a copy of b.py:
import a
# All the code from the original b, for example:
print("b done",a.HELLO))

это импортирует все пространство имен из c в b, а также бумагу по круговому импорту.

Я понимаю, что это отвратительно, поэтому никому об этом не говорите.


циклическая зависимость модуля обычно является запахом кода.

Это указывает на то, что часть кода должна быть пересчитана так, чтобы она была внешней для обоих модулей.


Итак, если я правильно читаю ваш прецедент,logging обращается к configuration для получения данных конфигурации. Однако,configuration имеет некоторые функции, которые при вызове требуют этого от logging импортируется в configuration.

если это так (то есть,configuration Не нужно logging пока вы не начнете вызывать функции), ответ прост: в configuration поместить весь импорт из logging в нижней части файла, после всего класса, функции и константы определение.

Python читает вещи сверху вниз: когда он сталкивается с import заявление configuration, он запускает его, но на данный момент,configuration уже существует как модуль, который можно импортировать, даже если он еще не полностью инициализирован: он имеет только атрибуты, которые были объявлены до import заявление было бежать.

Я согласен с другими, хотя, что круговой импорт обычно является запахом кода.