Условная ошибка Python «объект модуля не имеет атрибута» с личным пакетом, отличным от циклического импорта - PullRequest
13 голосов
/ 24 января 2012

Я получаю сообщение об ошибке «объект модуля не имеет атрибута ...» при попытке использовать созданную мной иерархию пакетов. Эта ошибка напоминает ошибку, которую вы получаете при циклическом импорте (т. Е. Модуль a импортирует 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

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

import alpha.bravo.charlie

Проблема появляется, если я пытаюсь импортировать alpha.bravo.charlie в alpha / bravo / __ init__.py (с мыслью, что я могу вспомнить соответствующие классы / методы там, и скрипт выполнит «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), но редактирую все, чтобы исключить уровень 'alpha' иерархии, похоже, он работает нормально.

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

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

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

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

python -c 'import bravo'
1

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

Ответы [ 2 ]

13 голосов
/ 24 января 2012

Вот почему

(Это, я думаю, в основном подтверждается объяснением на 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's namespace
return module 'a'

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

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

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

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

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

Вот что происходит

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

  1. Настройка пустого объекта модуля для альфы в sys.modules

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

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

  4. Запустите код в альфа / браво / __ init__.py:

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

    4.2 Запустите код в альфа / браво / чарли / __ init__.py:

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

    4.2.2 Запустите код в альфа / браво / эхо / __ init__.py:

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

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

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

На этом этапе, если мы вызовем dir () для всех модулей в sys.modules, мы увидим это:

  • 'альфа': ['__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']

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

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

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

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

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

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

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

from alpha.bravo.delta import delta_foo

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

1 голос
/ 24 января 2012

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

т.е.

альфа / браво / _ INIT _. Py

import alpha.bravo.charlie

должно быть

import charlie

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

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

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

...