Почему «модуль импорта», а затем «из модуля импорта пакетов» снова загружает модуль? - PullRequest
13 голосов
/ 08 июня 2011

У меня в 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 это "package.module". Итак, я думаю, что я хочу знать, почему здесь другой ключ - почему путь к файлу не используется в качестве ключа, чтобы Python делал то, что от него ожидают?

Ответы [ 2 ]

2 голосов
/ 09 июня 2011

Это незначительный дефект текущей модульной системы.

При импорте module вы делаете это из текущего пространства имен, у которого нет имени.значения внутри этого пространства имен такие же, как в package , но интерпретатор не может этого знать.

При импорте package.module вы импортируете модуль из пространства имен package .

Это причина того, что main.py должен быть снаружипакет для отправки.Многие модули имеют такую ​​организацию:

package /
    main.py
    package /
        sub_package1/
        sub_package2/
        sub_package3/
        module1.py
        module2.py

Вызов только main.py, чтобы убедиться, что пространства имен установлены правильно, иначе говоря текущее пространство имен - это main.py's .Это делает невозможным вызов import module1.py в module2.py.Вам нужно позвонить import package.module1 .Делает вещи проще и однороднее.

И да, импортировать текущую папку как текущую безымянную папку было плохой идеей.Это PITA, если вы выходите за рамки нескольких сценариев.Но когда Python начал там, он не был совершенно бессмысленным.

2 голосов
/ 09 июня 2011

Фактически, запустив код из каталога 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('').

...