У меня ответил вопрос об абсолютном импорте в 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 changelog, мы находим это (выделено мной):
В 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
будет всегда найти версию 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 to pkg
и запускать импорт непосредственно из REPL. Ни один из этих вариантов (а также их комбинация) не изменили результаты выше. Я не могу смириться с тем, что я прочитал о директиве __future__
и абсолютном импорте.
Мне кажется, что это легко объясняется следующим (это из документов Python 2, но это утверждение остается неизменным в том же docs для Python 3):
sys.path
(...)
Как инициализировано при запуске программы, первым элементом этого списка,
path[0]
, является каталог, содержащий script, который использовался для вызова интерпретатора Python. Если каталог script недоступен (например, если интерпретатор вызывается интерактивно или если script считывается со стандартного ввода),path[0]
- это пустая строка , которая направляет Python на поиск модулей в текущей сначала.
Так что мне не хватает? Почему оператор __future__
, по-видимому, не делает то, что он говорит, и какова резолюция этого противоречия между этими двумя разделами документации, а также между описанным и фактическим поведением?