Я відповів на питання щодо абсолютного імпорту в 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__
твердження, здавалося б, не робить того, що воно говорить, і яке вирішення цього протиріччя між цими двома розділами документації, а також між описаною та реальною поведінкою?