Udało mi się odpowiedzieć na pytanie dotyczące bezwzględnego importu w Pythonie, które, jak sądziłem, zrozumiałem na podstawie czytania dziennik zmian Pythona 2.5 i towarzyszący mu PEP. Jednak po zainstalowaniu Pythona 2.5 i próbie stworzenia przykładu prawidłowego użycia z __future__ import absolute_import
, zdaję sobie sprawę, że sprawy nie są takie jasne.
Prosto z powyższego dziennika zmian, to oświadczenie dokładnie podsumowało moje zrozumienie bezwzględnej zmiany importu:
Powiedzmy, że masz taki katalog pakietów:
pkg/pkg/__init__.py pkg/main.py pkg/string.py
To definiuje pakiet o nazwie
pkg
zawierającypkg.main
ipkg.string
.Rozważ kod w module main.py. Co się stanie, jeśli wykona on instrukcję
import string
? W Pythonie 2.4 i wcześniejszych najpierw zajrzy do katalogu pakietu, aby wykonać import względny, znajdzie plik pkg/string.py, zaimportuje zawartość tego pliku jako modułpkg.string
i ten moduł zostanie powiązany z nazwą"string"
w nazwach modułupkg.main
pace.
Więc stworzyłem dokładnie taką strukturę katalogów:
$ ls -R .: pkg/ ./pkg: __init__.py main.py string .py
__init__.py
i string.py
są puste. main.py
zawiera następujący kod:
import string print string.ascii_uppercase
Zgodnie z oczekiwaniami, uruchomienie tego w Pythonie 2.5 nie powiedzie się z AttributeError
:
$ python2.5 pkg/main.py Traceback (ostatnie wywołanie ostatnie): Plik "pkg/main.py", wiersz 2 , w <module> print string.ascii_uppercase AttributeError: obiekt "module" nie ma atrybutu "ascii_uppercase"
Jednak w dalszej części dziennika zmian 2.5 znajdujemy to (podkreślenie dodane):
W Pythonie 2.5 możesz zmienić zachowanie
import
na import bezwzględny za pomocą dyrektywyfrom __future__ import absolute_import
. To zachowanie importu bezwzględnego stanie się domyślne w przyszłą wersję (prawdopodobnie Python 2.7). Gdy importy bezwzględne będą wartościami domyślnymi,ciąg importu
zawsze znajdzie wersję standardowej biblioteki.
W ten sposób stworzyłem pkg/main2.py
, identyczny z main.py
, ale z dodatkową przyszłą dyrektywą importu. Teraz wygląda to tak:
from __future__ import absolute_import import string print string.ascii_uppercase
Uruchomienie tego w Pythonie 2.5, jednak... kończy się niepowodzeniem z AttributeError
:
$ python2.5 pkg/main2.py Traceback (ostatnie wywołanie ostatnie): Plik "pkg/main2.py", wiersz 3, w < ;moduł> print string.ascii_uppercase AttributeError: obiekt "module" nie ma atrybutu "ascii_uppercase"
To dość kategorycznie przeczy stwierdzeniu, że import string
będzie zawsze znajdź wersję std-lib z włączonym importem bezwzględnym. Co więcej, pomimo ostrzeżenia, że import bezwzględny ma stać się zachowaniem „nowym domyślnym”, napotkałem ten sam problem, używając zarówno Pythona 2.7, z dyrektywą __future__
, jak i bez niej:
$ python2.7 pkg/main.py Traceback (ostatnie wywołanie ostatnie): Plik "pkg/main.py", wiersz 2, w <module> print string.ascii_uppercase AttributeError: obiekt "module" nie ma atrybutu "ascii_uppercase" $ python2.7 pkg/main2.py Traceback (ostatnie wywołanie ostatnie): Plik "pkg/main2.py", wiersz 3, w <module> print string.ascii_uppercase AttributeError: obiekt "module" nie ma atrybutu "ascii_uppercase"
jak również Python 3.5, z lub bez (zakładając, że instrukcja print
została zmieniona w obu plikach):
$ python3.5 pkg/main.py Traceback (ostatnie wywołanie ostatnie): Plik "pkg/main.py", wiersz 2, w <module> print(string.ascii_uppercase) AttributeError: moduł "string " nie ma atrybutu "ascii_uppercase" $ python3.5 pkg/main2.py Trac eback (ostatnie wywołanie najnowsze): Plik „pkg/main2.py”, wiersz 3, w <module> print(string.ascii_uppercase) AttributeError: moduł "string" nie ma atrybutu "ascii_uppercase"
Testowałem inne odmiany tego. Zamiast string.py
utworzyłem pusty moduł -- katalog o nazwie string
zawierający tylko pusty __init__.py
-- i zamiast tego wydawania importów z main.py
, mam cd
"d do pkg
i uruchamiam importy bezpośrednio z REPL. Żadna z tych odmian (ani ich połączenie) zmieniło powyższe wyniki. Nie mogę pogodzić tego z tym, co przeczytałem o dyrektywie __future__
i imporcie bezwzględnym.
Wydaje mi się, że można to łatwo wytłumaczyć przez następujący (pochodzi z dokumentacji Pythona 2, ale instrukcja pozostaje niezmieniona w tej samej dokumentacji dla Pythona 3):
sys.path
(...)
Jako zainicjowane podczas uruchamiania programu, pierwsza pozycja na tej liście,
path[0]
, to katalog zawierający skrypt, który został użyty do wywołania interpretera Pythona. y nie jest dostępne (np. jeśli interpreter jest wywoływany interaktywnie lub jeśli skrypt jest odczytywany ze standardowego wejścia),path[0]
jest pustym ciągiem, który kieruje Pythona do wyszukiwania modułów w najpierw bieżący katalog.
Czego więc mi brakuje? Dlaczego instrukcja __future__
pozornie nie robi tego, co mówi, i jakie jest rozwiązanie tej sprzeczności między tymi dwoma sekcjami dokumentacji, a także między opisanym a rzeczywistym zachowaniem?