Циклическая зависимость импорта в 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. (И это на самом деле не существует, потому что мы были в середине импорта.)



как можно решить эту проблему?

590   5  

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

    Ничего не найдено.