Относительные проблемы импорта в Python 3 - PullRequest
12 голосов
/ 07 ноября 2011

Импорт Python сводит меня с ума (мой опыт работы с импортом Python иногда совсем не соответствует идиоме «Явный лучше, чем неявный» :():

[app]
    start.py
        from package1 import module1
    [package1]
        __init__.py
            print('Init package1')
        module1.py
            print('Init package1.module1')
            from . import module2
        module2.py
            print('Init package1.module2')
            import sys, pprint
            pprint.pprint(sys.modules)
            from . import module1

Я получаю:

vic@ubuntu:~/Desktop/app2$ python3 start.py 
Init package1
Init package1.module1
Init package1.module2
{'__main__': <module '__main__' from 'start.py'>,
 ...
 'package1': <module 'package1' from '/home/vic/Desktop/app2/package1/__init__.py'>,
 'package1.module1': <module 'package1.module1' from '/home/vic/Desktop/app2/package1/module1.py'>,
 'package1.module2': <module 'package1.module2' from '/home/vic/Desktop/app2/package1/module2.py'>,
 ...
Traceback (most recent call last):
  File "start.py", line 3, in <module>
    from package1 import module1
  File "/home/vic/Desktop/app2/package1/module1.py", line 3, in <module>
    from . import module2
  File "/home/vic/Desktop/app2/package1/module2.py", line 5, in <module>
    from . import module1
ImportError: cannot import name module1
vic@ubuntu:~/Desktop/app2$ 

import package1.module1 работает, но я хочу использовать from . import module1, потому что я хочу сделать package1 переносимым для других моих приложений, поэтому я хочу использовать относительные пути.

Я использую Python 3.

Мне нужен круговой импорт. Функция в module1 утверждает, что один из ее параметров является экземпляром класса, определенного в module2, и наоборот.

Другими словами:

sys.modules содержит 'package1.module1': <module 'package1.module1' from '/home/vic/Desktop/app2/package1/module1.py'>. Я хочу получить ссылку на него в форме from . import module1, но он пытается получить имя, а не пакет, как в случае import package1.module1 (который работает нормально). Я попытался import .module1 as m1 - но это синтаксическая ошибка.

Кроме того, from . import module2 в module1 работает нормально, но from . import module1 в module2 не работает ...

UPDATE:

Этот хак работает (но я ищу "официальный" способ):

print('Init package1.module2')

import sys, pprint
pprint.pprint(sys.modules)

#from . import module1
parent_module_name = __name__.rpartition('.')[0]
module1 = sys.modules[parent_module_name + '.module1']

Ответы [ 7 ]

6 голосов
/ 09 ноября 2011

Лучшее решение вашей проблемы - поместить package1 в отдельный пакет. Конечно, тогда он не может импортировать package2, но с другой стороны, если его можно использовать повторно, зачем ему это?

6 голосов
/ 07 ноября 2011

Циркулярный импорт обычно следует избегать, см. Также этот ответ на связанный вопрос или эту статью на effbot.org .

В этом случае проблемаВы импортируете from ., где . - текущий пакет.Таким образом, все ваши from . import X импорты проходят через пакет __init__.py.

. Вы можете сделать вашу проблему более заметной, если явно импортируете свои модули в __init__.py и дадите им другое имя (и отрегулируетедругие, конечно же, используют эти имена):

print('Init package1')
from . import module1 as m1
from . import module2 as m2

Теперь, когда вы импортируете m1 в start.py, пакет сначала инициализирует m1 и доходит до строки from . import m2.На этом этапе в __init__.py нет известных m2, поэтому вы получаете ошибку импорта.Если вы переключите операторы импорта в __init__.py (то есть сначала загрузите m2), то в m2 он найдет строку from . import m1, которая завершается ошибкой по той же причине, что и раньше.

ЕслиВы явно не импортируете модули в __init__.py, что-то подобное все еще происходит в фоновом режиме.Разница в том, что вы получаете менее плоскую структуру (поскольку импорт больше не начинается только из пакета).Таким образом, module1 и module2 запускаются, и вы получаете соответствующие отпечатки инициализации.

Чтобы это работало, вы можете выполнить абсолютный импорт в module2.Таким образом, вы можете избежать того, что пакет должен сначала разрешить все, и заставить его повторно использовать импорт из start.py (так как он имеет тот же путь импорта).

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

(надеюсь, мое объяснение имеет какой-то смысл, у меня уже были трудности при его написании, но общая идея должна быть ясной, янадеюсь ...)

edit

В ответ на ваше обновление;то, что вы делаете, это то, что вы используете полное имя пакета, чтобы получить ссылку на модуль.Это эквивалентно (но намного сложнее) первому возможному варианту заставить его работать;Вы используете абсолютный импорт, используя тот же путь импорта, что и в start.py.

2 голосов
/ 07 ноября 2011

Ваше обновление имитирует то, что делает абсолютный импорт: import package1.module1, если вы делаете это, пока module1 импортируется.Если вы хотите использовать динамическое имя родительского пакета, то для импорта module1 в module2.py:

import importlib
module1 = importlib.import_module('.module1', __package__)

мне нужен циклический импорт.Функция в module1 утверждает, что один из ее параметров является экземпляром класса, определенного в module2 и наоборот.

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

.
├── start.py
#       from package1 import module1
└── package1
    ├── __init__.py
#           print("Init package1")
#           from . import module1, module2
    ├── c1.py
#           print("Init package1.c1")
#           class C1:
#               pass
    ├── module1.py
#           print("Init package1.module1")
#           from .c1 import C1
#           from .module2 import C2
    └── module2.py
#           print("Init package1.module2")
#           from .c1 import C1
#           class C2:
#               pass
#           def f():
#               from .module1 import C1

Выход

Init package1
Init package1.module1
Init package1.c1
Init package1.module2

Другой вариант, который может быть проще, чем рефакторинг из c1.py, - объединить module{1,2}.py в один common.py.В этом случае module{1,2}.py делает импорт из common.

1 голос
/ 07 ноября 2011
module2.py
          import module1

Работает тоже.

0 голосов
/ 25 октября 2017

Убедитесь, что ваш package1 является папкой.Создайте класс в __init__.py - скажем class1.Включите вашу логику в метод под class1 - скажем method1.

Теперь напишите следующий код -

from .package1 import class1
class1.method1()

Это был мой способ его решения.Подводя итог, ваш корневой каталог - ., поэтому напишите свой оператор import, используя обозначения ., например from .package или from app.package.

0 голосов
/ 29 августа 2017

Я столкнулся с этой же проблемой сегодня, и кажется, что она действительно не работает в python3.4, но работает в python3.5.

В changelog есть запись:

Круговой импорт, включающий относительный импорт, теперь поддерживается.(Предоставлено Бреттом Кэнноном и Антуаном Питру в bpo-17636 ).

Просматривая отчет об ошибках, кажется, что это не столько исправлено, сколько исправленоособенность в способе импорта работы.Ссылаясь на ответ poke выше , он показывает, что from . import foo означает загрузить __init__.py и получить из него foo (возможно, из неявно загруженного списка подмодулей).Поскольку python3.5, from . import foo будет делать то же самое, но если foo недоступен в качестве атрибута, он обратится к просмотру списков загруженных модулей (sys.modules), чтобы узнать, присутствует ли он там уже., который исправляет этот конкретный случай.Я не уверен на 100%, что правильно представил, как это работает.

0 голосов
/ 07 ноября 2011

Принятый ответ на Круговая зависимость от импорта в Python дает хорошее замечание:

Если a зависит от c, а c зависит от a, не являются ли они фактически одной и той же единицей?

Вы должны действительно изучить, почему вы разделили a и c на два пакета, потому что либо у вас есть некоторый код, который вы должны разделить на другой пакет (чтобы они оба зависели от этого нового пакета, но не друг от друга), либо следует объединить их в одну упаковку.
& Mdash; Лассе В. Карлсен ♦

Может быть, вам следует рассмотреть возможность размещения их в одном модуле. :)

...