Я ответил на вопрос об абсолютном импорте в Python, который, как мне показалось, я понял, прочитав журнал изменений Python 2.5 и сопровождающий его PEP . Однако после установки Python 2.5 и попытки создать пример правильного использования from __future__ import absolute_import
я понимаю, что все не так ясно.
Прямо из журнала изменений, указанного выше, это утверждение точно подытожило мое понимание абсолютного изменения импорта:
Допустим, у вас есть каталог пакетов, подобный этому:
pkg/ pkg/__init__.py pkg/main.py pkg/string.py
Это определяет пакет с именем ,
pkg
содержащейpkg.main
иpkg.string
подмодули.Рассмотрим код в модуле main.py. Что произойдет, если он выполнит оператор
import string
? В Python 2.4 и более ранних версиях он сначала просматривает каталог пакета для выполнения относительного импорта, находит pkg / string.py, импортирует содержимое этого файла какpkg.string
модуль, и этот модуль привязывается к имени"string"
вpkg.main
пространстве имен модуля.
Итак, я создал эту точную структуру каталогов
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
и string.py
пусты. main.py
содержит следующий код:
import string
print string.ascii_uppercase
Как и ожидалось, запуск этого с Python 2.5 завершится неудачно с AttributeError
:
$ python2.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Однако далее в журнале изменений 2.5 мы находим это (выделение добавлено):
В Python 2.5 вы можете переключать
import
поведение на абсолютный импорт, используяfrom __future__ import absolute_import
директиву. Такое поведение абсолютного импорта станет будущим по умолчанию в будущей версии (возможно, Python 2.7). Как только абсолютный импорт по умолчанию,import string
всегда найдет версию стандартной библиотеки.
Таким образом, я создал pkg/main2.py
, идентичный, main.py
но с дополнительной директивой импорта в будущем. Теперь это выглядит так:
from __future__ import absolute_import
import string
print string.ascii_uppercase
Запуск этого с Python 2.5, однако ... не удается с AttributeError
:
$ python2.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Это довольно категорически противоречит тому , что import string
будет всегда найти версию станд-LIB с поддержкой абсолютного импорта. Более того, несмотря на предупреждение о том, что абсолютный импорт должен стать «новым режимом по умолчанию», я столкнулся с той же проблемой, используя оба языка Python 2.7, с __future__
директивой или без нее:
$ python2.7 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
$ python2.7 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
а также Python 3.5, с или без (при условии, что print
оператор изменяется в обоих файлах):
$ python3.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
$ python3.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
Я проверил другие варианты этого. Вместо этого string.py
я создал пустой модуль - каталог с именем, string
содержащим только пустой __init__.py
- и вместо того, чтобы выдавать импорт из main.py
, я должен cd
был pkg
выполнять импорт непосредственно из REPL. Ни один из этих вариантов (ни их комбинация) не изменил результаты выше. Я не могу совместить это с тем, что я прочитал о __future__
директиве и абсолютном импорте.
Мне кажется, что это легко объяснить следующим (это из документов Python 2, но это утверждение остается неизменным в тех же документах для Python 3):
sys.path
(...)
Как инициализируется при запуске программы, первым элементом этого списка
path[0]
является каталог, содержащий скрипт, который использовался для вызова интерпретатора Python. Если каталог скриптов недоступен (например, если интерпретатор вызывается в интерактивном режиме или если скрипт читается из стандартного ввода),path[0]
это пустая строка, которая направляет Python для поиска модулей в текущем каталоге в первую очередь.
Так чего мне не хватает? Почему __future__
утверждение, по-видимому, не соответствует тому, что оно говорит, и каково разрешение этого противоречия между этими двумя разделами документации, а также между описанным и фактическим поведением?
источник
Ответы:
Список изменений небрежно сформулирован.
from __future__ import absolute_import
не заботится о том, является ли что-то частью стандартной библиотеки, иimport string
не всегда дает вам модуль стандартной библиотеки с абсолютным импортом.from __future__ import absolute_import
означает, что если выimport string
, Python всегда будет искатьstring
модуль верхнего уровня , а неcurrent_package.string
. Однако это не влияет на логику, которую Python использует, чтобы решить, какой файл являетсяstring
модулем. Когда вы делаетеpkg/script.py
не выглядит как часть пакета для Python. Следуя обычным процедурам,pkg
каталог добавляется в путь, и все.py
файлы вpkg
каталоге выглядят как модули верхнего уровня.import string
находитpkg/string.py
не потому, что выполняет относительный импорт, а потому, чтоpkg/string.py
кажется модулем верхнего уровняstring
. Тот факт, что это неstring
модуль стандартной библиотеки , не подходит.Чтобы запустить файл как часть
pkg
пакета, вы можете сделатьВ этом случае
pkg
каталог не будет добавлен к пути. Тем не менее, текущий каталог будет добавлен в путь.Вы также можете добавить шаблон, чтобы
pkg/script.py
Python рассматривал его как частьpkg
пакета, даже если он запускается как файл:Однако это не повлияет
sys.path
. Вам понадобится дополнительная обработка, чтобы удалитьpkg
каталог из пути, и еслиpkg
родительский каталог не находится в пути, вам нужно будет также вставить его в путь.источник
import string
если вы случайно замаскируете его, по крайней мере, не пробираясь сквозь негоsys.modules
. Разве это не то, чтоfrom __future__ import absolute_import
предназначено для предотвращения? Что оно делает? (PS, я не downvoter.)sys.path
работает, а сам вопрос вообще не был рассмотрен. То есть, что наfrom __future__ import absolute_import
самом деле делает?sys.modules
не даст вамstring
модуль стандартной библиотеки, если вы замаскируете его собственным модулем верхнего уровня.from __future__ import absolute_import
не предназначен для того, чтобы блокировать модули верхнего уровня от теневого копирования модулей верхнего уровня; он должен помешать внутренним модулям пакетов скрывать модули верхнего уровня. Если вы запустите файл как частьpkg
пакета, внутренние файлы пакета перестанут отображаться как верхний уровень.pkg
что это пакет в пути поиска импорта, который должен бытьpython -m pkg.main
.-m
нужно имя модуля, а не путь к файлу.Разница между абсолютным и относительным импортом вступает в силу только тогда, когда вы импортируете модуль из пакета, и этот модуль импортирует другой субмодуль из этого пакета. Увидеть разницу:
В частности:
Обратите внимание, что
python2 pkg/main2.py
поведение отличается от запуска,python2
а затем импортаpkg.main2
(что эквивалентно использованию-m
переключателя).Если вы когда-нибудь захотите запустить подмодуль пакета, всегда используйте
-m
переключатель, который запрещает интерпретатору объединять цепочки вsys.path
список и правильно обрабатывает семантику подмодуля.Кроме того, я предпочитаю использовать явный относительный импорт для подмодулей пакетов, так как они предоставляют больше семантики и лучшие сообщения об ошибках в случае сбоя.
источник
python2 pkg/main2.py
поведение отличается от запуска python2 и импорта pkg.main2?pkg/main2.py
python (версия 2) не рассматриваетсяpkg
как пакет. При использованииpython2 -m pkg.main2
или импортировать его делать принять во внимание , чтоpkg
это пакет.