Циклические зависимости модулей и относительный импорт в Python
предположим, что у нас есть два модуля с циклическими зависимостями:
# a.py
import b
def f(): return b.y
x = 42
# b.py
import a
def g(): return a.x
y = 43
два модуля находятся в каталоге pkg
пустой __init__.py
. Импорт pkg.a
или pkg.b
работает отлично, как объяснено в ответ. Если я изменю импорт на относительный импорт
from . import b
Я ImportError
при попытке импортировать один из модулей:
>>> import pkg.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pkg/a.py", line 1, in <module>
from . import b
File "pkg/b.py", line 1, in <module>
from . import a
ImportError: cannot import name a
почему я получаю эту ошибку? Не красивая ситуация так же, как и выше? (Это связано с этот вопрос?)
редактировать: этот вопрос не о разработке программного обеспечения. Я знаю способы избежать циклической зависимости, но меня все равно интересует причина ошибки.
3 ответов
сначала давайте начнем с того, как from import
работа в python:
Ну сначала давайте посмотрим на байтовый код:
>>> def foo():
... from foo import bar
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 2 (('bar',))
6 IMPORT_NAME 0 (foo)
9 IMPORT_FROM 1 (bar)
12 STORE_FAST 0 (bar)
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
Хм интересно :), так что from foo import bar
переводится на first IMPORT_NAME foo
что эквивалентно import foo
а то IMPORT_FROM bar
.
теперь что IMPORT_FROM
делать ?
давайте посмотрим, что делает python, когда он нашел IMPORT_FROM
:
TARGET(IMPORT_FROM)
w = GETITEM(names, oparg);
v = TOP();
READ_TIMESTAMP(intr0);
x = import_from(v, w);
READ_TIMESTAMP(intr1);
PUSH(x);
if (x != NULL) DISPATCH();
break;
Ну в основном он получает имена, чтобы импорт из, который находится в нашем будет bar
, затем он выскакивает из стека кадров значение v
что возвращение последней операции выполняются который IMPORT_NAME
, затем вызвать функцию import_from()
С этими двумя аргументами:
static PyObject *
import_from(PyObject *v, PyObject *name)
{
PyObject *x;
x = PyObject_GetAttr(v, name);
if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Format(PyExc_ImportError, "cannot import name %S", name);
}
return x;
}
Как видите,import_from()
функция тихая легкая, она пытается сначала получить атрибут name
в модуле v
, если он не существует, он поднимает ImportError
else верните это атрибут.
теперь, что это имеет отношение к относительному импорту ?
Ну относительный импорт, как from . import b
эквивалентны, например, в случае, если в вопросе OP from pkg import b
.
но как это произошло ? Чтобы понять это, мы должны взглянуть на import.c
модуль python специально для функции get_parent(). Как вы видите, функция тихая долго перечислять здесь, но в целом, что это делает, когда он видит относительный импорт, чтобы попытаться заменить точку .
с родительским пакетом в зависимости от __main__
модуль, который снова из вопроса OP является пакетом pkg
.
теперь давайте сложим все это вместе и попытаемся выяснить, почему мы в конечном итоге с поведением в вопросе OP.
для этого это поможет нам, если мы сможем увидеть, что делает python при импорте, ну, это наш счастливый день python уже с этой функцией, которая может быть включена запустив его в режиме extra verbose -vv
.
Итак, используя командную строку:python -vv -c 'import pkg.b'
:
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
import pkg # directory pkg
# trying pkg/__init__.so
# trying pkg/__init__module.so
# trying pkg/__init__.py
# pkg/__init__.pyc matches pkg/__init__.py
import pkg # precompiled from pkg/__init__.pyc
# trying pkg/b.so
# trying pkg/bmodule.so
# trying pkg/b.py
# pkg/b.pyc matches pkg/b.py
import pkg.b # precompiled from pkg/b.pyc
# trying pkg/a.so
# trying pkg/amodule.so
# trying pkg/a.py
# pkg/a.pyc matches pkg/a.py
import pkg.a # precompiled from pkg/a.pyc
# clear[2] __name__
# clear[2] __file__
# clear[2] __package__
# clear[2] __name__
# clear[2] __file__
# clear[2] __package__
...
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "pkg/b.py", line 1, in <module>
from . import a
File "pkg/a.py", line 2, in <module>
from . import a
ImportError: cannot import name a
# clear __builtin__._
Хм, что только что произошло до ImportError
?
первая) from . import a
на pkg/b.py
называется, что переводится как описано выше для from pkg import a
, который снова в байт-коде эквивалентен import pkg; getattr(pkg, 'a')
. Но подождите минутку!--33--> - это тоже модуль ?!
Ну вот пришла забавная часть, если у нас есть что-то вроде from module|package import module
in в этом случае произойдет второй импорт, который является импортом модуля в предложении import. Итак, снова в Примере OP нам нужно импортировать pkg/a.py
, и, как вы знаете, прежде всего мы установили в нашем sys.modules
ключ для нашего нового модуля, который будет pkg.a
и затем мы продолжаем нашу интерпретацию модуля pkg/a.py
, но перед модулем pkg/a.py
завершить импорт вызова from . import b
.
наступила секунду) часть pkg/b.py
импортируется и в свою очередь это будет первая попытка import pkg
, потому что pkg
уже импортирован, поэтому есть ключ pkg
в нашем sys.modules
он просто вернет значение этого ключа. Тогда это будет import b
установить pkg.b
ключ в sys.modules
и начать толкование. И мы подходим к этой линии from . import a
!
но помните pkg/a.py
уже импортировано, что означает ('pkg.a' in sys.modules) == True
таким образом, импорт будет пропущен, и только getattr(pkg, 'a')
будет называться , но что будет дальше ? питон не закончил импорт pkg/a.py
!? Так только getattr(pkg, 'a')
будет вызван, и это поднимет AttributeError
на import_from()
функция, которая будет переведена на ImportError(cannot import name a)
.
снимаем: это мои собственные усилия, чтобы понять, что происходит внутри переводчика, я далек от того, чтобы быть экспертом.
EDIt: этот ответ был перефразирован, потому что, когда я попытался прочитать его снова, я заметил, как мой ответ был плохо сформулирован, надеюсь, теперь это будет полезнее:)
(Incedentally, относительный импорт не имеет значения. Используя from pkg import
... показывает то же исключение.)
Я думаю, что здесь происходит то, что разница между from foo import bar
и import foo.bar
заключается в том, что в первый, стоимостью bar
может быть модуль в pkg foo
или это может быть переменная в модуле foo
. Во втором случае, это недопустимо для bar
быть чем угодно, кроме модуля / пакета.
это имело бы значение, потому что если bar известен как модуль, тогда содержание sys.modules
достаточно, чтобы заполнить его. Если это может быть переменная в foo
модуль, тогда интерпретатор должен фактически заглянуть в содержимое foo
, но при импорте foo
, это было бы недопустимо; фактический модуль еще не заполнен.
в случае относительного импорта мы понимаем from . import bar
чтобы импортировать модуль bar из пакета, содержащего текущий модуль, но это действительно просто синтаксический сахар,.
имя переводится в полное имя и передается в __import__()
, и таким образом это выглядит для всего мира как неоднозначные from foo import bar
в качестве дополнительного Примечания:
у меня была следующая структура модуля:
base
+guiStuff
-gui
+databaseStuff
-db
-basescript
Я хотел иметь возможность запускать свой скрипт import base.basescript
, однако это не удалось с ошибкой, так как у import base.databaseStuff.db
что вызвало импорт base
. С base
был зарегистрирован только как __main__
это вызвало второе выполнение всего импорта и ошибку выше, если я не использовал внешний скрипт выше импорт base
/ basescript
только один раз. К предотвратить это я помещаю следующее в мой базовый скрипт:
if __name__ == '__main__' or \
not '__main__' in sys.modules or \
sys.modules['__main__'].__file__ != __file__:
#imports here