Почему "импортировать модуль", а затем "из модуля импорта пакетов" загружает модуль снова?
У меня есть пакет в PYTHONPATH, который выглядит примерно так:
package/
__init__.py
module.py
print 'Loading module'
Если я запускаю Python из каталога package/
(или записываю другой модуль в этот каталог) и набираю
import module
загружает module.py и выводит "Загрузочный модуль", как и ожидалось. Однако, если я тогда набираю
from package import module
он загружает module.py и снова печатает "Загрузочный модуль", чего я не ожидаю. Какое обоснование для этого?
Примечание. Я понимаю, что я понимаю, почему Python делает это, потому что ключ sys.modules для import module
- это просто "module"
, но для from package import module
it "package.module"
. Поэтому я предполагаю, что я хочу знать, почему ключ здесь различен - почему не имя пути к файлу, используемое в качестве ключа, так что Python делает то, что вы ожидаете здесь?
Ответы
Ответ 1
Фактически, запустив код из каталога package
, вы неправильно сконфигурировали Python. Вы не должны помещать этот каталог в sys.path
, так как он внутри пакета.
Python не использует имя файла в качестве ключа, потому что он не импортирует файл, а импортирует модуль. Предоставление людям возможности "import c:\jim\my files\projects\code\stuff
" поощряло бы все виды гадости.
Рассмотрим этот случай: что, если вы были в ~/foo/package/
и ~/bar
, были на PYTHONPATH
- но ~/bar просто символическая ссылка на ~/foo
? Ожидаете ли вы, что Python решит, а затем дедуплицируйте символическую ссылку для вас? Что, если вы поместите относительный каталог в PYTHONPATH, а затем смените каталоги? Что, если "foo.py" является символической ссылкой на "bar.py"? Вы ожидаете, что и те, и другие будут дедуплицированы? Что, если они не символические ссылки, а просто точные копии? Добавление сложных правил, чтобы попытаться сделать что-то удобное в двусмысленных обстоятельствах, означает, что он делает что-то очень неудобное для других людей. (Python zen 12: перед лицом двусмысленности откажитесь от соблазна угадать.)
Python делает здесь что-то простое, и ваша ответственность - убедиться, что среда настроена правильно. Теперь вы можете утверждать, что по умолчанию это не очень хорошая идея поместить текущий каталог на PYTHONPATH
- я мог бы даже согласиться с вами - но учитывая, что он есть, он должен следовать тому же согласованному набору правил, что и другой путь записи делают. Если он предназначен для запуска из произвольного каталога, ваше приложение всегда может удалить текущий каталог из sys.path
, начиная с sys.path.remove('')
.
Ответ 2
Это незначительный дефект текущей системы модулей.
При импорте модуля вы делаете это из текущего пространства имен, у которого нет имени. значения внутри этого пространства имен те же, что и в пакете, но интерпретатор не может этого знать.
При импорте package.module вы импортируете модуль из пространства имен пакетов.
Это причина, по которой main.py должен находиться вне пакета forlder.
Многие модули имеют эту организацию:
package /
main.py
package /
sub_package1/
sub_package2/
sub_package3/
module1.py
module2.py
Вызывая только main.py, убедитесь, что пространства имен правильно установлены, aka текущее пространство имен - это main.py's. Это делает невозможным вызов import module1.py в module2.py. Вам нужно вызвать import package.module1. Делает вещи более простыми и однородными.
И да, импортируйте текущую папку, поскольку текущая безымянная папка была плохой идеей.
Это PITA, если вы выходите за рамки нескольких сценариев. Но поскольку Python начал там, это было не совсем бессмысленно.