Bezwzględny a jawny względny import modułu Pythona

85

Zastanawiam się nad preferowanym sposobem importowania pakietów w aplikacji Python. Mam taką strukturę pakietu:

project.app1.models
project.app1.views
project.app2.models

project.app1.viewsimport project.app1.modelsi project.app2.models. Przychodzą mi na myśl dwa sposoby, aby to zrobić.

W przypadku importu bezwzględnego:

import A.A
import A.B.B

lub z wyraźnym importem względnym, jak wprowadzono w Pythonie 2.5 z PEP 328 :

# explicit relative
from .. import A
from . import B

Jaki jest najbardziej pythonowy sposób na zrobienie tego?

Daniel Hepper
źródło
„jawne względne” przykłady to błędy składniowe. Import względne muszą być w formie from _ import ..., dzięki czemu byłyby przykłady from .. import Aifrom . import B
MestreLion
@MestreLion Dobry chwyt, masz całkowitą rację! Zaktualizowałem moje pytanie z import ..Ana from .. import A. Niezwykłe, że minęło tylko 9 lat, zanim ktoś zauważył;)
Daniel Hepper

Odpowiedzi:

52

Absolutny import. Od PEP 8:

Względny import w przypadku importu wewnątrz paczki jest wysoce odradzany. Zawsze używaj bezwzględnej ścieżki pakietu dla wszystkich importów. Nawet teraz, gdy PEP 328 [7] jest w pełni zaimplementowany w Pythonie 2.5, jego styl jawnego importu względnego jest aktywnie odradzany; import bezwzględny jest bardziej przenośny i zwykle bardziej czytelny.

Jawne importy względne to fajna funkcja języka (chyba), ale nie są tak wyraźne, jak import absolutny. Bardziej czytelna forma to:

import A.A
import A.B.B

zwłaszcza jeśli importujesz kilka różnych przestrzeni nazw. Jeśli spojrzysz na dobrze napisane projekty / samouczki, które zawierają import z pakietów, zwykle są one zgodne z tym stylem.

Kilka dodatkowych naciśnięć klawiszy, które przyjmujesz, aby były bardziej wyraźne, pozwoli zaoszczędzić innym (i być może Tobie) dużo czasu w przyszłości, gdy będą próbowali rozgryźć twoją przestrzeń nazw (zwłaszcza jeśli dokonasz migracji do 3.x, w której część pakietu nazwy uległy zmianie).

Rafe Kettler
źródło
@Rafe, „spójrz na dobrze napisane projekty…” jakieś sugestie?
denis
@Denis: Rietveld to własny projekt Guido van Rossuma, więc wyobrażam sobie, że byłoby to dobre miejsce do obejrzenia ( code.google.com/p/rietveld ). Standardowa biblioteka Pythona nie jest tak świetna, wiele z tego kodu nie jest zgodnych z konwencjami.
Rafe Kettler
68
@Rafe: według Guido ta część PEP-8 jest przestarzała. mail.python.org/pipermail/python-dev/2010-October/104476.html
Brandon Rhodes
12
Tego stwierdzenia nie ma już w ogóle w PEP-8. Obecnie stwierdza się, że import bezwzględny jest zalecany, ale import względny jest akceptowalną alternatywą.
dano
6
Problem, który mam z importami bezwzględnymi, polega na używaniu pakietu w innym pakiecie. W moim przypadku jest obecny jako podmoduł git. W tym przypadku, chociaż mogę zaimportować pakiet najwyższego poziomu, żadnych pakietów poniżej tego nie można zaimportować, ponieważ nie znajdują własnych modułów z importem bezwzględnym. Natomiast jeśli użyję importu względnego na tym dolnym poziomie, wszystko po prostu działa.
davidA
122

Względne importowanie w Pythonie nie jest już zdecydowanie odradzane, ale w takim przypadku zdecydowanie zaleca się użycie bezwzględnego importu.

Zobacz tę dyskusję, cytując samego Guido:

„Czy to nie jest w większości historyczne? Do czasu wprowadzenia nowej składni importu względnego istniały różne problemy z importem względnym. Krótkoterminowym rozwiązaniem było zalecenie ich niestosowania. Długoterminowym rozwiązaniem było zaimplementowanie jednoznacznej składni. Teraz nadszedł czas, aby wycofać anty-zalecenie. Oczywiście, nie przesadzając - nadal uważam je za nabyte, ale mają swoje miejsce ”.

OP poprawnie łączy PEP 328, który mówi:

Zaprezentowano kilka przypadków użycia, z których najważniejszym jest możliwość zmiany struktury dużych pakietów bez konieczności edytowania pakietów podrzędnych. Ponadto moduł wewnątrz pakietu nie może łatwo zaimportować się bez importu względnego.

Zobacz także prawie zduplikowane pytanie Kiedy i dlaczego używać importu względnego w Pythonie

Oczywiście nadal pozostaje kwestią gustu. Chociaż łatwiej jest przenosić kod za pomocą względnych importów, może to również nieoczekiwanie zepsuć wszystko; a zmiana nazwy importu nie jest taka trudna.

Aby wymusić nowe zachowanie z PEP 328, użyj:

from __future__ import absolute_import

W takim przypadku niejawny import względny nie będzie już możliwy (np. import localfileNie będzie już działać, tylko from . import localfile). Aby zachować czyste i odporne na przyszłość zachowanie, zaleca się korzystanie z bezwzględnego importu.

Ważnym zastrzeżeniem jest to, że ze względu na PEP 338 i PEP 366 , import względny wymaga, aby plik Pythona został zaimportowany jako moduł - nie można wykonać pliku file.py, który ma import względny, w przeciwnym razie otrzymasz plik ValueError: Attempted relative import in non-package.

To ograniczenie należy wziąć pod uwagę przy ocenie najlepszego podejścia. Guido jest przeciwny uruchamianiu skryptów z modułu w każdym przypadku:

Jestem -1 w tej i wszystkich innych proponowanych twiddlach maszyny __main__. Jedynym przypadkiem użycia wydaje się być uruchamianie skryptów, które znajdują się w katalogu modułu, co zawsze postrzegałem jako anty-wzór. Żeby zmienić zdanie, musiałbyś mnie przekonać, że tak nie jest.

Wyczerpujące dyskusje na ten temat można znaleźć na SO; re. Python 3 to dość obszerne:

Stefano
źródło
9
Guido napisał to w 2010 roku i nadal jest w PEP? Jak możemy ufać PEP, skoro są tak przestarzali?
Jabba,
2
PEP są jak poprawki w USA w tym sensie, że można coś zmienić. Jest też wiele odrzuconych PEPS. PEP to propozycje. Mogą zostać zaakceptowane, odrzucone lub przestarzałe, co często oznacza nowego PEP. PEP 8 to przewodnik po stylu, więc można go modyfikować na miejscu.
CppLearner
2
Jestem zdezorientowany co do części „moduł wewnątrz pakietu nie może łatwo zaimportować się ...”. Nigdy wcześniej nie słyszałem o importowaniu się modułów.
matiascelasco
2
Jeden możliwy przykład @matiascelasco: jeśli masz foo / bar.py i foo / baz.py, ale także baz.py gdzie indziej. Jeśli chcesz zaimportować foo.baz z paska, możesz być pewien, co importujesz, np. import .baz- to tylko jedna uproszczona z wielu podobnych sytuacji opisanych w PEP.
Stefano,
Twoja odpowiedź nie pozwala wyraźnie odróżnić zmiany w ich dopuszczaniu. Niejawne importy względne nigdy nie powinny być używane, ale jawne importy względne są dopuszczalne. Niejawny krewny został usunięty z Python3.
ninMonkey
33

Importy względne nie tylko pozostawiają swobodę późniejszej zmiany nazwy pakietu bez zmiany dziesiątek wewnętrznych importów, ale także udało mi się rozwiązać pewne problemy związane z takimi rzeczami, jak import cykliczny lub pakiety przestrzeni nazw, ponieważ nie wysyłają one Pythona z powrotem do top ”, aby ponownie rozpocząć wyszukiwanie następnego modułu z przestrzeni nazw najwyższego poziomu.

Brandon Rhodes
źródło
4
To jest styl odradzany zgodnie z przewodnikiem po stylu Pythona. Znacznie utrudniają one czytelność i nie są warte postrzeganej „wygody”, o której wspominasz. Jeśli musisz użyć importu względnego, aby rozwiązać problem, robisz to źle.
Rafe Kettler
14
Zwróć uwagę na jego komentarz (Brandon Rhodes) dotyczący drugiej odpowiedzi, z linkiem wskazującym, że nie jest to już zniechęcane.
Jon Coombs
1
@RafeKettler Czy możesz wyjaśnić, w jaki sposób użyłbyś importu bezwzględnego w pakiecie, który sam jest zawarty w innym pakiecie? Bezwzględny import nie powiedzie się w pakiecie wewnętrznym, ponieważ nie wiedzą o nowym najwyższym poziomie. Względny import nadal działa. Ktoś mógłby prawdopodobnie argumentować, że pakiet nie powinien być zagnieżdżony w innym w pierwszej kolejności, ale część kodu jest przeznaczona do wielokrotnego użytku i zdarza się to często. Wiele ponownie wykorzystanych kodów nie jest pakowanych dla publiczności i dlatego nie jest dostarczanych jako oddzielny pakiet, więc zamiast tego używane są metody ad-hoc, takie jak import / podmoduły VCS
davidA
3
@meowsqueak Zgadzam się, niektórych pakietów nie da się łatwo zainstalować (nie ma ich na pip, nie chcesz ich używać python setup.py installlub python setup.py developz jakiegokolwiek powodu), w takich przypadkach rozwidlam kod źródłowy i dodaję go jako podmoduł git. Gdy te pakiety używają importu bezwzględnego dla własnej nazwy pakietu, ich importowanie kończy się niepowodzeniem. Jedynym rozwiązaniem jest użycie jawnego importu względnego. Myślę, że to powinno być zachęcane.
CMCDragonkai