Циклическая зависимость импорта в Python
допустим, у меня есть следующая структура каталогов:
a
__init__.py
b
__init__.py
c
__init__.py
c_file.py
d
__init__.py
d_file.py
на a пакета __init__.py на импортируется. Но c_file.py импорт a.b.d.
программа не работает, говоря:b не существует, когда c_file.py пытается импортировать a.b.d. (И это на самом деле не существует, потому что мы были в середине импорта.)
как можно решить эту проблему?
5 ответов:
Если a зависит от c и c зависит от a, разве они на самом деле не одна и та же единица?
вы должны действительно изучить, почему вы разделили a и c на два пакета, потому что либо у вас есть какой-то код, который вы должны разделить на другой пакет (чтобы они оба зависели от этого нового пакета, но не друг от друга), либо вы должны объединить их в один пакет.
вы можете отложить импорт, например, в
a/__init__.py:def my_function(): from a.b.c import Blah return Blah()то есть отложите импорт до тех пор, пока он действительно не понадобится. Тем не менее, я бы также внимательно посмотрел на мои определения/использования пакетов, поскольку циклическая зависимость, подобная указанной, может указывать на проблему проектирования.
Я задавался этим вопросом пару раз (обычно при работе с моделями, которые должны знать друг о друге). Простое решение - просто импортировать весь модуль, а затем ссылаться на то, что вам нужно.
поэтому вместо того, чтобы делать
from models import Studentи
from models import Classroomв другом, просто сделать
import modelsв одном из них, а затем назвать моделями.Класс, когда вам это нужно.
проблема в том, что при запуске из каталога по умолчанию только пакеты, которые являются подкаталогами, видны как импорт кандидатов, поэтому вы не можете импортировать a. b. d.однако вы можете импортировать b. d. поскольку b является подпакетом a.
если вы действительно хотите импортировать a.b. d в
c/__init__.pyвы можете сделать это, изменив системный путь на один каталог выше a и изменив импорт вa/__init__.pyдля импорта a. b. c.код
a/__init__.pyдолжно выглядеть это:import sys import os # set sytem path to be directory above so that a can be a # package namespace DIRECTORY_SCRIPT = os.path.dirname(os.path.realpath(__file__)) sys.path.insert(0,DIRECTORY_SCRIPT+"/..") import a.b.cдополнительная трудность возникает, когда вы хотите запустить модули на C как скрипты. Здесь пакеты a и b не существуют. Вы можете взломать
__int__.pyв каталоге c, чтобы указать sys.путь к каталогу верхнего уровня, а затем импортировать__init__в любых модулях внутри c, чтобы иметь возможность использовать полный путь для импорта a.b. d. я сомневаюсь, что это хорошая практика для импорта__init__.pyно он работал для моего случая использования.
другое решение-использовать прокси для d_file.
например, предположим, что вы хотите поделиться классом blah с c_file. Таким образом, d_file содержит:
class blah: def __init__(self): print("blah")вот что вы вводите c_file.py:
# do not import the d_file ! # instead, use a place holder for the proxy of d_file # it will be set by a's __init__.py after imports are done d_file = None def c_blah(): # a function that calls d_file's blah d_file.blah()и в ainit. py:
from b.c import c_file from b.d import d_file class Proxy(object): # module proxy pass d_file_proxy = Proxy() # now you need to explicitly list the class(es) exposed by d_file d_file_proxy.blah = d_file.blah # finally, share the proxy with c_file c_file.d_file = d_file_proxy # c_file is now able to call d_file.blah c_file.c_blah()
Comments