Условный "объект модуля" класса Python не имеет атрибута "с личным пакетом, отличным от циклической задачи импорта

Я получаю ошибку "объект модуля не имеет атрибута..." при попытке использовать созданную мной иерархию пакетов. Ошибка является напоминанием об ошибке, которую вы получаете, когда имеется циклический импорт (т.е. модуль импорта b и модуль b импортирует a), но я не вижу этой проблемы здесь. Я прошел через много сообщений с аналогичной ошибкой, но ни одно из объяснений, которое я видел вполне подходящим.

Это было замечено с помощью python 2.7.1 и python 2.4.3.

Я опустил его до следующего примера:

Рассмотрим следующую иерархию (см. код ниже):

alpha
alpha/__init__.py
alpha/bravo
alpha/bravo/__init__.py
alpha/bravo/charlie.py
alpha/bravo/delta.py
alpha/bravo/echo.py

Модуль charlie импортирует эхо, которое в свою очередь импортирует delta. Если альфа /bravo/ __ init__.py(например, alpha/__ init__.py) по существу пуста, script может выполнять:

import alpha.bravo.charlie

Проблема возникает, если я пытаюсь импортировать alpha.bravo.charlie в alpha/bravo/__ init__.py(с мышлением я мог бы нарисовать соответствующие классы/методы там, а script будет делать "import alpha.bravo" ).

код:

альфа/__ __ INIT. Ру

(blank)

альфа/браво/__ __ INIT. Ру

import alpha.bravo.charlie

альфа/браво/charlie.py

import alpha.bravo.echo
def charlie_foo(x): return str(x)
def charlie_bar(x): return alpha.bravo.echo.echo_biz()

альфа/браво/delta.py

def delta_foo(x): return str(x)

альфа/браво/echo.py

import alpha.bravo.delta
print alpha.bravo.delta.delta_foo(1)
def echo_biz(): return 'blah'

Если я попробую:

python -c 'import alpha.bravo'

Я получаю:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/__init__.py", line 1, in <module>
    import alpha.bravo.charlie
  File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/charlie.py", line 1, in <module>
    import alpha.bravo.echo
  File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/echo.py", line 2, in <module>
    print alpha.bravo.delta.delta_foo(1)
AttributeError: 'module' object has no attribute 'bravo'

Но, если я прокомментирую строку импорта в alpha/bravo/__ init__.py, тогда все кажется ОК:

python -c 'import alpha.bravo'

python -c 'import alpha.bravo.charlie'
1

Кроме того, если я использую тот же самый код выше (включая строку импорта в alpha/bravo/__ init__.py), но отредактируйте все, чтобы исключить уровень "альфа" иерархии, он работает нормально.

Итак, иерархия теперь просто:

bravo
bravo/__init__.py
bravo/charlie.py
bravo/delta.py
bravo/echo.py

и я меняю все строки на "alpha.bravo. *" на "bravo. *"

Тогда не проблема:

python -c 'import bravo'
1

Мне удалось решить проблему, но я все равно хотел бы ее понять. Спасибо.

Ответ 1

Вот почему

(Это, я считаю, в основном поддерживается объяснением на http://docs.python.org/faq/programming.html#how-can-i-have-modules-that-mutually-import-each-other)

Когда интерпретатор Python встречает строку формы import a.b.c, она выполняет следующие шаги. В псевдо-питоне:

for module in ['a', 'a.b', 'a.b.c']:
    if module not in sys.modules:
        sys.modules[module] = (A new empty module object)
        run every line of code in module # this may recursively call import
        add the module to its parent namespace
return module 'a'

Здесь есть три важных момента:

  • Модули a, a.b и a.b.c импортируются по порядку, если они еще не были импортированы

  • Модуль не существует в своем родительском пространстве имен, пока он полностью не будет импортирован. Таким образом, модуль a не имеет атрибута b до тех пор, пока a.b не будет полностью импортирован.

  • Независимо от того, насколько глубоко ваша цепочка модулей, даже если вы import a.b.c.d.e.f.g, ваш код получает только один символ, добавленный в его пространство имен: a.

    Поэтому, когда вы позже попытаетесь запустить a.b.c.d.e.f.g.some_function(), интерпретатор должен пройти весь путь вниз по цепочке модулей, чтобы перейти к этому методу.

Здесь что происходит

На основе кода, который вы опубликовали, проблема, похоже, лежит в операторе печати в alpha/bravo/echo/__init__.py. То, что сделал интерпретатор к тому моменту, когда оно появляется, примерно такое:

  • Настройте пустой объект модуля для alpha в sys.modules

  • Запустите код в alpha/__ init__.py(обратите внимание, что dir (alpha) не будет содержать "bravo" в этой точке)

  • Настройте пустой объект модуля для alpha.bravo в sys.modules

  • Запустите код в alpha/bravo/__ init __. py:

    4.1 Настройте пустой объект модуля для alpha.bravo.charlie в sys.modules

    4.2 Запустите код в alpha/bravo/charlie/__ init __. py:

    4.2.1. Установите пустой объект модуля для альфа/браво/эхо в sys.modules

    4.2.2 Запустите код в alpha/bravo/echo/__ init __. py:

    4.2.2.1 Настройте пустой объект модуля для альфа /bravo/delta в sys.modules

    4.2.2.2 Запустите код в alpha/bravo/delta/__ init__.py - это заканчивается, поэтому 'delta' добавляется к символам alpha.bravo.

    4.2.2.3. Добавьте альфа-символы к эхо-символам. Это последний шаг в import alpha.bravo.delta.

В этот момент, если мы будем называть dir() для всех модулей в sys.modules, мы увидим следующее:

  • 'alpha': ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__'] (это по существу пусто)

  • 'alpha.bravo': ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'delta'] (дельта закончила импорт, поэтому он здесь)

  • 'alpha.bravo.charlie': ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__'] (пусто)

  • 'alpha.bravo.delta': ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__', 'delta.foo'] (Это единственный, который завершился)

  • 'alpha.bravo.echo': ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__', 'alpha']

Теперь intrereter продолжается с альфа /bravo/echo/ __ init__.py, где он встречает строку print alpha.bravo.delta.delta_foo(1). Это начало этой последовательности:

  • получить глобальную переменную alpha - это возвращает все еще пустой модуль alpha.
  • call getattr (alpha, 'bravo') - это не удается, потому что alpha.bravo еще не закончен, но инициализирован, поэтому bravo не был вставлен в таблицу альфа-символов.

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

Если вы должны были заменить строку нарушения в echo/__ init__.py следующим образом:

import sys
sys.modules['alpha.bravo.delta'].delta_foo(1)

Это, вероятно, будет работать, поскольку дельта полностью инициализирована. Но до тех пор, пока bravo не будет завершен (после возврата эха и charlie), таблица символов для альфы не будет обновляться, и вы не сможете получить доступ к ней через нее.

Кроме того, как говорит @Ric Poggi, если вы измените строку импорта на

from alpha.bravo.delta import delta_foo

Тогда это сработает. В этом случае, поскольку from alpha.bravo.delta идет прямо в sys.modules dict, а не переходит от альфы к bravo в delta, он может получить функцию из модуля дельта и назначить ее локальной переменной, к которой вы можете получить доступ без любая проблема.

Ответ 2

Вместо использования абсолютного импорта он может помочь использовать родственников.

то есть.

альфа/браво/_init_.py

import alpha.bravo.charlie

должен быть

import charlie

В противном случае это, вероятно, круговой импорт. т.е. если вы импортируете alpha.bravo.charlie из charlie, это означает

alpha/__init__.py
bravo/__init__.py
charlie/__init__.py 

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