Что на самом деле делает абсолютный импорт из будущего?
я ответил вопрос относительно абсолютного импорта в 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
module, и этот модуль привязан к имени"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 changelog, мы находим это (акцент добавлено):
в Python 2.5, вы можете переключиться
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
будет всегда найдите версию std-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
' d к pkg
и запустить импорт непосредственно из REPL. Ни один из этих вариантов (ни комбинация из них) изменила результаты выше. Я не могу примирить это с тем, что я читал о __future__
директива и абсолютный импорт.
мне кажется, что это легко объяснимый следующее (это из документов Python 2, но этот оператор остается неизменным в тех же документах для Python 3):
sys.путь
(...)
как инициализируется при запуске программы, первым пунктом этого лист,
path[0]
, это каталог, содержащий скрипт, который использовался для вызова интерпретатора Python. Если каталог скриптов недоступен (например, если интерпретатор вызывается интерактивно или если скрипт считывается со стандартного ввода),path[0]
это пустая строка,который сначала направляет Python на поиск модулей в текущем каталоге.
так что я упускаю? Почему __future__
заявление, по-видимому, не делает то, что он говорит, и что такое разрешение этого противоречия между этими двумя разделами документации, а также между описанным и реальным поведением?
2 ответов
список изменений небрежно сформулирован. from __future__ import absolute_import
не заботится о том, что является частью стандартной библиотеки, и import string
не всегда будет давать вам модуль стандартной библиотеки с абсолютным импортом.
from __future__ import absolute_import
означает, что если вы import string
, Python всегда будет искать верхний уровень string
модуль, а не current_package.string
. Однако это не влияет на логику, используемую Python для определения того, какой файл string
модуль. Когда вы делаете
python pkg/script.py
pkg/script.py
не похоже на часть пакета для Python. Следуя обычным процедурам,pkg
каталог добавляется в путь, и все .py
файлы pkg
каталог выглядит как модули верхнего уровня. import string
находит pkg/string.py
не потому, что он делает относительный импорт, а потому что pkg/string.py
кажется, модуль верхнего уровня string
. Тот факт, что это не стандартная библиотека string
модуль не появляется.
чтобы запустить файл как часть , вы могли бы do
python -m pkg.script
в этом случае pkg
каталог не будет добавлен в путь. Однако в путь будет добавлен текущий каталог.
вы также можете добавить некоторые шаблонные в pkg/script.py
чтобы Python рассматривал его как часть pkg
пакет даже при запуске в виде файла:
if __name__ == '__main__' and __package__ is None:
__package__ = 'pkg'
однако это не повлияет sys.path
. Вам понадобится дополнительная обработка, чтобы удалить pkg
каталог из Пути, и если pkg
родительский каталог не на пути, вам нужно будет придерживаться этого на пути тоже.
разница между абсолютным и относительным импортом вступает в игру только при импорте модуля из пакета, и этот модуль импортирует другой подмодуль из этого пакета. Смотрите разницу:
$ mkdir pkg
$ touch pkg/__init__.py
$ touch pkg/string.py
$ echo 'import string;print(string.ascii_uppercase)' > pkg/main1.py
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pkg/main1.py", line 1, in <module>
import string;print(string.ascii_uppercase)
AttributeError: 'module' object has no attribute 'ascii_uppercase'
>>>
$ echo 'from __future__ import absolute_import;import string;print(string.ascii_uppercase)' > pkg/main2.py
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>>
в частности:
$ python2 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 1, in <module>
from __future__ import absolute_import;import string;print(string.ascii_uppercase)
AttributeError: 'module' object has no attribute 'ascii_uppercase'
$ python2
Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>>
$ python2 -m pkg.main2
ABCDEFGHIJKLMNOPQRSTUVWXYZ
отметим, что python2 pkg/main2.py
имеет другое поведение, то запуск python2
и затем импортировать pkg.main2
(что эквивалентно помощью -m
переключатель).
если вы когда-либо хотите запустить подмодуль пакета всегда используйте -m
переключатель, который предотвращает интерпретатор для chaning sys.path
список и правильно обрабатывает семантику подмодуля.
кроме того, я предпочитаю использовать явный относительный импорт для подмодулей пакетов, поскольку они предоставляют больше семантики и лучшие сообщения об ошибках в случае сбоя.