Как исправить циклическую зависимость для импорта - PullRequest
0 голосов
/ 18 сентября 2010

У меня есть три файла:

модуль testimports:

#import moduleTwo
import moduleOne

hiString = "Hi!"

moduleOne.sayHi()

moduleOne:

import moduleTwo

class sayHi():
    moduleTwo.printHi()

moduleTwo:

import testimports

def printHi():
    print(testimports.hiString)

Если язапустите testimports, я получу:

Traceback (most recent call last):
  File "..file path snipped../testimports/src/testimports.py", line 2, in <module>
    import moduleOne
  File "..file path snipped../testimports/src/moduleOne.py", line 1, in <module>
    import moduleTwo
  File "..file path snipped../testimports/src/moduleTwo.py", line 1, in <module>
    import testimports
  File "..file path snipped../testimports/src/testimports.py", line 6, in <module>
    moduleOne.sayHi()
AttributeError: 'module' object has no attribute 'sayHi'

Если, однако, я раскомментирую строку import moduleTwo в testimports, программа доберется до этой точки, прежде чем перестанет работать:

Traceback (most recent call last):
  File "..file path snipped../testimports/src/testimports.py", line 1, in <module>
    import moduleTwo
  File "..file path snipped../testimports/src/moduleTwo.py", line 1, in <module>
    import testimports
  File "..file path snipped../testimports/src/testimports.py", line 2, in <module>
    import moduleOne
  File "..file path snipped../testimports/src/moduleOne.py", line 3, in <module>
    class sayHi():
  File "..file path snipped../testimports/src/moduleOne.py", line 4, in sayHi
    moduleTwo.printHi()
AttributeError: 'module' object has no attribute 'printHi'

Как мне решить проблему циклической зависимости?

Ответы [ 3 ]

9 голосов
/ 18 сентября 2010

verisimilidude в правильном направлении. Я бы расширил немного, чтобы дать больше деталей.

В обоих случаях происходит следующее:

  1. testimports выполняется как __main__
  2. testimports импорт moduleOne. Теперь moduleOne читается из файла и добавляется в список импортируемых модулей sys.modules.
  3. Выполнение импорта moduleOne начинается с импорта moduleTwo. Теперь moduleTwo читается из файла и добавляется в список импортируемых модулей sys.modules. Обратите внимание, что на этом этапе остальная часть moduleOne еще не выполнена, поэтому sayHi не определено.
  4. Теперь moduleTwo начинает выполнение с импорта testimports. Это первый раз, когда testimports импортируется, и он не имеет ничего общего с тем, что __main__. Теперь он вставлен в sys.modules.
  5. Здесь все становится интереснее. Недавно импортированный testimports импортирует moduleOne. Но moduleOne уже находится в sys.modules, поэтому он не читается снова. Затем выполнение переходит к строке moduleOne.sayHi(). Но импорт moduleOne еще не завершен, а sayHi еще не определен . Следовательно мы получаем ошибку.

Подобный цикл происходит, если moduleTwo не закомментирован. По сути, __main__ импортирует moduleTwo, который импортирует testimports, который проходит импорт moduleTwo, который уже импортирован, и импортирует moduleOne; который в свою очередь импортирует moduleTwo, а затем пытается вызвать moduleTwo.printHi, который еще не определен, потому что moduleTwo не завершил выполнение все это время.

Решение Анатолия Рра ломает все это, заставляя модуль testimports не вызывать другие функциональные возможности модуля при импорте. Поэтому, когда он импортируется moduleTwo, он не вызывает moduleOne.sayHi. Только при запуске как __main__ он будет выполняться так, что выполнение будет отложено после всех импортов.

Мораль этой истории? Избегайте круговых зависимостей в Python, если это возможно .

2 голосов
/ 18 сентября 2010

Ваша проблема в том, что когда Python импортирует что-то, он выполняет все операторы на базовом уровне. hiString назначается снова, и вызов повторяется, когда Module3 импортирует ваш исходный файл testimports.py в качестве модуля. Решение Анатолия Рра работает, потому что вызов сейчас внутри def. Значение def не вызывается, поскольку __name__ указывает среде выполнения Python, что модуль импортируется. Когда он вызывается из командной строки, его имя модуля будет __main__.

2 голосов
/ 18 сентября 2010

Перезапись testimports.py может помочь:

import moduleOne

hiString = "Hi!"

def main ():
    moduleOne.sayHi()

if __name__ == "__main__":
    main ()
...