Как правильно обрабатывать циклическую зависимость модуля в Python?
попытка найти хороший и правильный шаблон для обработки круговой зависимости модуля в Python. Обычно решение состоит в том, чтобы удалить его (через рефакторинг); однако в этом конкретном случае мы действительно хотели бы иметь функциональность, требующую кругового импорта.
редактировать: согласно ответам ниже, обычным углом атаки для такого рода проблем будет рефакторинг. Однако, ради этого вопроса, предположим, что это не вариант (для любая причина.)
проблема:
The logging
модуль требует configuration
модуль для данных конфигурации. Однако для некоторых из configuration
функции я бы очень хотел использовать пользовательские функции ведения журнала, которые определены в logging
модуль. Очевидно, что импорт logging
модуль configuration
выдает ошибку.
возможные решения мы можем думать из:
не надо. Как я уже говорил, это не лучший вариант, если только все остальные возможности не уродливы и плохи.
Monkey-исправьте модуль. Это звучит не так уж плохо: нагрузка
logging
модуль динамически вconfiguration
после начальный импорт, и перед тем, как любая из его функций фактически используются. Это подразумевает определение глобальных переменных для каждого модуля, хотя.инъекции зависимостей. Я прочитал и столкнулся с альтернативами инъекций зависимостей (особенно в корпоративном пространстве 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
заявление было бежать.
Я согласен с другими, хотя, что круговой импорт обычно является запахом кода.