Класс импорта Python с тем же именем, что и каталог

Допустим, у меня есть следующий макет исходного файла Python:

lib/foo.py
lib/foo/bar.py

а затем в моем исходном коде:

from foo import gaz

Я получаю ошибку импорта:

ImportError: No module named foo

Как я могу иметь файл .py и каталог с тем же именем, чтобы я мог сделать следующее:

 from foo import gaz
 from foo.bar import wakawaka

заранее спасибо!


person David Williams    schedule 26.04.2013    source источник
comment
stackoverflow.com/questions/279237 /   -  person Moj    schedule 27.04.2013
comment
@Moj не то же самое. Этот пост посвящен относительному импорту. Проблема, которую я пытаюсь выяснить, заключается в том, как иметь файл .py с тем же именем, что и каталог в том же каталоге, а затем импортировать из .py   -  person David Williams    schedule 27.04.2013


Ответы (2)


Фактическая проблема, с которой вы столкнулись при использовании одного импорта, связана с пакетами имеющий приоритет над модулями:

Обратите внимание, что при использовании from package import item элемент может быть либо подмодулем (или подпакетом) пакета, либо каким-либо другим именем, определенным в пакете, например, функцией, классом или переменной. Оператор import сначала проверяет, определен ли элемент в пакете; если нет, он предполагает, что это модуль, и пытается загрузить его. Если его не удается найти, возникает исключение ImportError.

В любом случае, я бы настоятельно предложил переименовать файл или каталог, поскольку вы не можете импортировать более одного модуля с заданным именем. Проблема возникает из-за того, что каждый объект модуля/пакета хранится в sys.modules, который является простым dict и поэтому не может содержать несколько одинаковых ключей.

В частности, если предположить, что каталоги foo.py и foo находятся в разных каталогах (и если это не так, вы все равно не можете импортировать foo.py), при выполнении:

from foo import gaz

Он загрузит foo.py и поместит модуль в sys.modules, а затем попытается сделать:

from foo.bar import wakaka

Произойдет сбой, поскольку импорт пытается использовать модуль foo.py вместо пакета.

Противоположное произойдет, если вы сначала импортируете foo.bar; импорт будет использовать пакет вместо модуля.

person Bakuriu    schedule 26.04.2013
comment
Единственный оператор импорта, который я пытаюсь выполнить, — это from foo import gaz, где класс gaz определен в foo.py. Вы говорите, что невозможно иметь foo.py и foo/bar.py, а затем импортировать функцию из foo.py? - person David Williams; 27.04.2013
comment
@DavidWilliams Я обновил. Ошибка, которую вы видите, связана с порядком импорта, хотя мой первый ответ все еще актуален, когда вы имеете дело с модулями/пакетами с тем же именем. - person Bakuriu; 27.04.2013
comment
Я думаю, что на самом деле мы могли бы говорить о немного разных вещах. Попробуйте следующее: touch foo.py && mkdir foo && touch foo/__init__.py && touch foo/bar.py создайте класс Foo в foo.py и Bar в bar.py. Затем попробуйте импортировать $ python -c "from foo import Foo" Traceback (most recent call last): File "<string>", line 1, in <module> ImportError: cannot import name Foo - person David Williams; 27.04.2013
comment
@DavidWilliams Код в вашем комментарии просто подтверждает то, что я утверждаю в своем ответе. Пакет foo загружается, а затем не может найти имя Foo. Ответ на ваш вопрос заключается в том, что вы не можете иметь foo.py и foo/__init__.py в одном каталоге и иметь рабочий import. - person Bakuriu; 27.04.2013
comment
Ну, я понимаю и принимаю. Похоже на бородавку на Python. Во многих языках OO обычно используется настройка этого файла и каталога для целей пространства имен, но это нормально. - person David Williams; 27.04.2013
comment
@DavidWilliams Я думаю, проблема в том, что python import полностью отличается от импорта большинства других языков. Например, в Java & co. довольно легко понять, является ли имя именем класса (следовательно, файла) или пакета (следовательно, каталога), и поэтому этой проблемы не существует. Я считаю, что этот выбор был сделан, потому что попытка более разумной семантики привела бы к гораздо большему количеству побочных эффектов (например, модули больше не являются одноэлементными, или пакеты должны стать совершенно другими объектами и т. д.). - person Bakuriu; 27.04.2013

Я почти уверен, что это не то, что вам следует делать, но вы можете заставить Python импортировать определенный файл как модуль, используя имп:

import imp

with open("foo.py") as source:
    foo = imp.load_module("foo", source, "./", ('', '', imp.PY_SOURCE))

теперь вы можете сделать:

gaz_instance = foo.gaz()
person Jacek Przemieniecki    schedule 26.04.2013
comment
Это работает только при условии, что рабочий каталог такой же, как и foo.py, но это может быть не так, поскольку каталог lib OP мог быть каким-то образом добавлен к sys.path. Чтобы получить каталог, вы должны вызвать imp.find_module('foo') и получить путь к файлу. Я не добавил это в свой ответ, потому что это большое изменение в том, как работает import, и его следует использовать только в случае необходимости (например, динамическая загрузка плагина/расширения во время выполнения). - person Bakuriu; 27.04.2013
comment
imp.find_module('foo') вернет информацию о пакете в каталоге /foo/ (при условии, что и foo.py, и /foo/ находятся в одном и том же каталоге) - person Jacek Przemieniecki; 27.04.2013