Что на самом деле делает абсолютный импорт из будущего?

я ответил вопрос относительно абсолютного импорта в 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 список и правильно обрабатывает семантику подмодуля.

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