Byłem tu:
- http://www.python.org/dev/peps/pep-0328/
- http://docs.python.org/2/tutorial/modules.html#packages
- Pakiety Python: import względny
- przykładowy kod importu względnego Pythona nie działa
- Ostateczna odpowiedź na względny import Pythona
- Względny import w Pythonie
- Python: Wyłączanie importu względnego
i mnóstwo adresów URL, których nie skopiowałem, niektóre na SO, inne na innych stronach, kiedy myślałem, że szybko znajdę rozwiązanie.
Zawsze powtarzające się pytanie brzmi: w systemie Windows 7, 32-bitowym Pythonie 2.7.3, jak rozwiązać ten komunikat „Próba względnego importu w pakiecie innym niż pakiet”? Zbudowałem dokładną replikę pakietu na pep-0328:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
Importy zostały wykonane z konsoli.
Zrobiłem funkcje o nazwie spam i jaja w odpowiednich modułach. Oczywiście to nie działało. Odpowiedź znajduje się w czwartym podanym adresie URL, ale dla mnie to wszystko jest absolwentami. Odpowiedź pojawiła się na jednym z adresów URL, które odwiedziłem:
Względne importy używają atrybutu nazwy modułu do ustalenia pozycji tego modułu w hierarchii pakietów. Jeśli nazwa modułu nie zawiera żadnych informacji o pakiecie (np. Jest ustawiona na „main”), importy względne są rozstrzygane tak, jakby moduł był modułem najwyższego poziomu, niezależnie od tego, gdzie moduł faktycznie znajduje się w systemie plików.
Powyższa odpowiedź wygląda obiecująco, ale dla mnie wszystkie hieroglify. Więc moje pytanie, jak sprawić, aby Python nie zwrócił mi „Próba importu względnego w pakiecie innym niż pakiet”? ma odpowiedź, która zawiera rzekomo -m.
Czy ktoś może mi powiedzieć, dlaczego Python wyświetla ten komunikat o błędzie, co to znaczy „nieopakowany”, dlaczego i jak definiuje się „pakiet”, a dokładna odpowiedź jest wystarczająco łatwa do zrozumienia dla przedszkola .
źródło
def spam(): pass
, moduleA.py madef eggs(): pass
. Próbowałem wykonać kilka poleceń „z. Coś importować coś”, ale one nie działały. Ponownie, patrz pep-0328.from .something import something
w interaktywnym tłumaczu, to nie zadziała. Względnego importu można używać tylko wewnątrz modułów, a nie interaktywnie.Odpowiedzi:
Skrypt vs. Moduł
Oto wyjaśnienie. Krótka wersja jest taka, że istnieje duża różnica między bezpośrednim uruchomieniem pliku Python a importowaniem tego pliku z innego miejsca. Sama wiedza o tym, w którym katalogu znajduje się plik, nie decyduje o tym, w którym pakiecie Python myśli, że jest. Zależy to dodatkowo od sposobu załadowania pliku do Pythona (przez uruchomienie lub zaimportowanie).
Istnieją dwa sposoby ładowania pliku Python: jako skrypt najwyższego poziomu lub jako moduł. Plik jest ładowany jako skrypt najwyższego poziomu, jeśli wykonasz go bezpośrednio, na przykład wpisując
python myfile.py
w wierszu poleceń. Jest ładowany jako moduł, jeśli takpython -m myfile
, lub jeśli jest ładowany, gdyimport
instrukcja napotkana jest w innym pliku. Jednocześnie może istnieć tylko jeden skrypt najwyższego poziomu; skrypt najwyższego poziomu to plik Python, który uruchomiłeś, aby rozpocząć.Nazewnictwo
Po załadowaniu plik otrzymuje nazwę (zapisaną w
__name__
atrybucie). Jeśli został załadowany jako skrypt najwyższego poziomu, jego nazwa to__main__
. Jeśli został załadowany jako moduł, jego nazwą jest nazwa pliku, poprzedzona nazwami dowolnych pakietów / podpakietów, których jest częścią, oddzielonych kropkami.Na przykład w twoim przykładzie:
jeśli zaimportowałeś
moduleX
(uwaga: zaimportowany , a nie bezpośrednio wykonany), jego nazwą byłobypackage.subpackage1.moduleX
. Jeśli zaimportujeszmoduleA
, jego nazwa topackage.moduleA
. Jeśli jednak uruchomisz bezpośredniomoduleX
z wiersza polecenia, jego nazwa będzie zamiast tego__main__
, a jeśli bezpośrednio uruchomiszmoduleA
z wiersza polecenia, będzie to nazwa__main__
. Kiedy moduł jest uruchamiany jako skrypt najwyższego poziomu, traci swoją normalną nazwę, a zamiast tego ma swoją nazwę__main__
.Dostęp do modułu NIE poprzez jego pakiet zawierający
Występuje dodatkowe pomarszczenie: nazwa modułu zależy od tego, czy został zaimportowany „bezpośrednio” z katalogu, w którym się znajduje, czy zaimportowany za pośrednictwem pakietu. Ma to znaczenie tylko wtedy, gdy uruchomisz Python w katalogu i spróbujesz zaimportować plik do tego samego katalogu (lub jego podkatalogu). Na przykład, jeśli uruchomisz interpreter języka Python w katalogu,
package/subpackage1
a następnie zrobiszimport moduleX
, nazwa pomoduleX
prostu będziemoduleX
, a niepackage.subpackage1.moduleX
. Wynika to z faktu, że Python dodaje bieżący katalog do ścieżki wyszukiwania podczas uruchamiania; jeśli znajdzie moduł do zaimportowania w bieżącym katalogu, nie będzie wiedział, że ten katalog jest częścią pakietu, a informacje o pakiecie nie staną się częścią nazwy modułu.Szczególnym przypadkiem jest interakcyjne uruchomienie interpretera (np. Po prostu wpisz
python
i zacznij wpisywać kod Pythona w locie). W tym przypadku nazwa tej interaktywnej sesji to__main__
.Oto kluczowa kwestia dla komunikatu o błędzie: jeśli nazwa modułu nie zawiera kropek, nie jest uważana za część pakietu . Nie ma znaczenia, gdzie plik faktycznie znajduje się na dysku. Liczy się tylko to, jak się nazywa, a nazwa zależy od sposobu załadowania.
Teraz spójrz na cytat zawarty w pytaniu:
Względny import ...
Import względne użyć modułu za nazwę , aby określić, gdzie znajduje się w opakowaniu. Gdy korzystasz z importu względnego
from .. import foo
, kropki wskazują na zwiększenie pewnej liczby poziomów w hierarchii pakietów. Na przykład, jeśli nazwa twojego bieżącego modułu topackage.subpackage1.moduleX
, to..moduleA
znaczypackage.moduleA
. Aby afrom .. import
działał, nazwa modułu musi zawierać co najmniej tyle kropek, ile jest wimport
instrukcji.... są tylko względne w pakiecie
Jednak jeśli nazwa twojego modułu to
__main__
, nie jest uważany za pakiet. Jego nazwa nie zawiera kropek, dlatego nie można w nim używaćfrom .. import
instrukcji. Jeśli spróbujesz to zrobić, pojawi się błąd „Względny import w pakiecie innym niż pakiet”.Skrypty nie mogą importować względnych
To, co prawdopodobnie zrobiłeś, to próba uruchomienia
moduleX
itp. Z wiersza poleceń. Gdy to zrobisz, jego nazwa zostanie ustawiona na__main__
, co oznacza, że względny import w nim nie powiedzie się, ponieważ jego nazwa nie ujawnia, że jest w pakiecie. Zauważ, że tak się stanie również, jeśli uruchomisz Python z tego samego katalogu, w którym znajduje się moduł, a następnie spróbujesz zaimportować ten moduł, ponieważ, jak opisano powyżej, Python znajdzie moduł w bieżącym katalogu „zbyt wcześnie”, nie zdając sobie sprawy, że jest część pakietu.Pamiętaj również, że po uruchomieniu interaktywnego tłumacza „nazwa” tej interaktywnej sesji jest zawsze taka sama
__main__
. Dlatego nie można wykonywać importów względnych bezpośrednio z sesji interaktywnej . Importów względnych można używać tylko w plikach modułów.Dwa rozwiązania:
Jeśli naprawdę chcesz uruchomić
moduleX
bezpośrednio, ale nadal chcesz, aby był traktowany jako część pakietu, możesz to zrobićpython -m package.subpackage1.moduleX
.-m
Mówi Python ładować go jako moduł, a nie jak w skrypcie najwyższego poziomu.A może tak naprawdę nie chcesz uruchomić
moduleX
, po prostu chcesz uruchomić inny skrypt, powiedzmymyfile.py
, który używa funkcji w środkumoduleX
. W takim przypadku umieśćmyfile.py
gdzie indziej - nie wpackage
katalogu - i uruchom go. Jeśli w środkumyfile.py
robisz takie rzeczyfrom package.moduleA import spam
, będzie dobrze.Notatki
Dla każdego z tych rozwiązań katalog pakietu (
package
w twoim przykładzie) musi być dostępny ze ścieżki wyszukiwania modułu Pythona (sys.path
). Jeśli tak się nie stanie, nie będziesz w stanie używać niczego w pakiecie.Od wersji Python 2.6 „nazwa” modułu do celów rozwiązywania pakietów jest określana nie tylko przez jego
__name__
atrybuty, ale także przez__package__
atrybut. Dlatego unikam używania jawnego symbolu__name__
w odniesieniu do „nazwy” modułu. Od wersji Python 2.6 „nazwa” modułu jest efektywna__package__ + '.' + __name__
lub tylko__name__
jeśli__package__
takNone
.)źródło
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.
Jest to zasadniczo niepokojące. Co jest takiego trudnego w przeglądaniu bieżącego katalogu? Python powinien mieć taką możliwość. Czy to naprawiono w wersji 3x?__package__ = 'package.subpackage1'
podobnego. Wtedy tylko ten plik będzie zawsze uważany za część tego pakietu, nawet jeśli zostanie uruchomiony bezpośrednio. Jeśli masz inne pytania,__package__
możesz zadać osobne pytanie, ponieważ omawiamy tutaj oryginalne pytanie.__name__
isys.path
. W szczególności, zpython -m pkg.mod
,__name__
jest ustawiony na__main__
niepkg.mod
; import względny jest rozstrzygany za pomocą__package__
raczej niż__name__
w tym przypadku. Ponadto Python dodaje do katalogu katalog skryptu, a nie katalogsys.path
bieżącypython path/to/script.py
; dodaje bieżący katalog dosys.path
większości innych sposobów, w tympython -m pkg.mod
.To jest naprawdę problem w Pythonie. Przyczyną zamieszania jest to, że ludzie błędnie przyjmują relatywny import jako relatywną ścieżkę, która nie jest.
Na przykład, gdy piszesz w faa.py :
Ma to znaczenie tylko wtedy, gdy plik faa.py został zidentyfikowany i załadowany przez python podczas wykonywania, jako część pakietu. W takim przypadku nazwą modułu dla faa.py będzie na przykład nazwa_pakietu.faa . Jeśli plik został załadowany tylko dlatego, że znajduje się w bieżącym katalogu, po uruchomieniu Pythona jego nazwa nie odnosiłaby się do żadnego pakietu i ostatecznie import względny nie powiódłby się.
Prostym rozwiązaniem do odsyłania modułów w bieżącym katalogu jest użycie tego:
źródło
from __future__ import absolute_import
i zmusza użytkownika do prawidłowego używania kodu ... abyś zawsze mógł to zrobićfrom . import foo
Oto ogólny przepis, zmodyfikowany tak, aby pasował jako przykład, którego używam teraz do pracy z bibliotekami Pythona napisanymi jako pakiety, które zawierają współzależne pliki, w których chcę móc testować ich fragmentaryczne fragmenty. Nazwijmy to
lib.foo
i powiedzieć, że to musi mieć dostęp dolib.fileA
do funkcjif1
if2
, alib.fileB
do klasyClass3
.Dołączyłem kilka
print
telefonów, aby zilustrować, jak to działa. W praktyce chciałbyś je usunąć (a może ifrom __future__ import print_function
linię).Ten konkretny przykład jest zbyt prosty, aby pokazać, kiedy naprawdę musimy wstawić wpis
sys.path
. (Zobacz odpowiedź Larsa na przypadek, w którym jej potrzebujemy, gdy mamy dwa lub więcej poziomów katalogów pakietów, a następnie używamyos.path.dirname(os.path.dirname(__file__))
- ale to też tak naprawdę nie boli .) Jest to również wystarczająco bezpieczne, aby to zrobić bezif _i in sys.path
test. Jednak jeśli każdy zaimportowany plik wstawi tę samą ścieżkę - na przykład, jeśli obiefileA
ifileB
chcą zaimportować narzędzia z pakietu - zaśmiecasys.path
to wiele razy tę samą ścieżkę, więc dobrze jest mieć jąif _i not in sys.path
na płycie głównej.Chodzi o to (i zauważ, że wszystkie one działają tak samo w python2.7 i python 3.x):
Jeśli uruchamiane jako
import lib
lubfrom lib import foo
jako zwykły zaimportować pakiet ze zwykłym kodzie,__package
tolib
i__name__
tolib.foo
. Bierzemy pierwszą ścieżkę kodu, importujemy z.fileA
itd.Jeśli zostanie uruchomiony jako
python lib/foo.py
,__package__
będzie Brak i__name__
będzie__main__
.Bierzemy drugą ścieżkę kodu.
lib
Katalog będzie już wsys.path
więc nie ma potrzeby, aby go dodać. Importujemy zfileA
itp.Jeśli zostanie uruchomiony w
lib
katalogu aspython foo.py
, zachowanie jest takie samo jak w przypadku 2.Jeśli jest uruchamiany w
lib
katalogu aspython -m foo
, zachowanie jest podobne do przypadków 2 i 3. Jednak ścieżka dolib
katalogu nie istniejesys.path
, więc dodajemy go przed importowaniem. To samo dotyczy, jeśli uruchomimy Python, a następnieimport foo
.(Ponieważ
.
jest na początkusys.path
, tak naprawdę nie musimy tutaj dodawać absolutnej wersji ścieżki. W tym miejscu głębsza struktura zagnieżdżania pakietów, tam gdzie chcemy to zrobićfrom ..otherlib.fileC import ...
, robi różnicę. Jeśli tego nie robisz, możeszsys.path
całkowicie pomiń całą manipulację).Notatki
Nadal istnieje dziwactwo. Jeśli uruchomisz to wszystko z zewnątrz:
lub:
zachowanie zależy od zawartości
lib/__init__.py
. Jeśli to istnieje i jest puste , wszystko jest dobrze:Ale jeśli
lib/__init__.py
sam importuje,routine
aby można było eksportowaćroutine.name
bezpośrednio jakolib.name
, otrzymasz:Oznacza to, że moduł jest importowany dwukrotnie, raz za pośrednictwem pakietu, a następnie ponownie
__main__
, aby uruchomićmain
kod. Python 3.6 i nowsze wersje ostrzegają o tym:Ostrzeżenie jest nowy, ale ostrzegł, o zachowanie nie jest. Jest to część tego, co niektórzy nazywają pułapką podwójnego importu . (Aby uzyskać dodatkowe informacje, patrz problem 27487 ). Nick Coghlan mówi:
Pamiętaj, że chociaż naruszamy tę zasadę tutaj, robimy to tylko wtedy, gdy ładowany plik nie jest ładowany jako część pakietu, a nasza modyfikacja została zaprojektowana specjalnie, aby umożliwić nam dostęp do innych plików w tym pakiecie. (I, jak zauważyłem, prawdopodobnie nie powinniśmy tego robić w przypadku pakietów jednopoziomowych.) Jeśli chcielibyśmy być wyjątkowo czysti, moglibyśmy przepisać to jako np .:
Oznacza to, że modyfikujemy
sys.path
wystarczająco długo, aby osiągnąć nasz import, a następnie ustawiamy go z powrotem tak, jak było (usuwając jedną kopię,_i
jeśli i tylko jeśli dodaliśmy jedną kopię_i
).źródło
Po zastanowieniu się nad tym wraz z wieloma innymi, natknąłem się na notatkę opublikowaną przez Doriana B w tym artykule, która rozwiązała konkretny problem, który miałem, gdzie opracowywałem moduły i klasy do użytku z usługą internetową, ale chcę też być mogłem je testować podczas pisania kodu, korzystając z narzędzi do debugowania w PyCharm. Aby uruchomić testy w samodzielnej klasie, na końcu mojego pliku klasy umieściłbym następujące:
ale gdybym chciał zaimportować inne klasy lub moduły w tym samym folderze, musiałbym wtedy zmienić wszystkie moje instrukcje importu z notacji względnej na referencje lokalne (tj. usunąć kropkę (.)) Ale po przeczytaniu sugestii Doriana spróbowałem jego „ one-liner ”i zadziałało! Mogę teraz testować w PyCharm i pozostawić mój kod testowy na miejscu, gdy korzystam z klasy w innej testowanej klasie lub kiedy używam jej w moim serwisie internetowym!
Instrukcja if sprawdza, czy korzystamy z tego modułu jako głównego, czy też jest używany w innym module, który jest testowany jako główny . Być może jest to oczywiste, ale oferuję tę notatkę tutaj, na wypadek gdyby ktoś inny sfrustrowany względnymi problemami z importem powyżej mógł z niej skorzystać.
źródło
Oto jedno rozwiązanie, którego nie poleciłbym, ale może być przydatne w niektórych sytuacjach, w których moduły po prostu nie zostały wygenerowane:
źródło
Miałem podobny problem, w którym nie chciałem zmieniać ścieżki wyszukiwania modułów Pythona i musiałem ładować moduł stosunkowo ze skryptu (pomimo „skryptów nie można zaimportować względnie wszystkich”, jak BrenBarn ładnie wyjaśnił powyżej).
Więc użyłem następującego hacka. Niestety, opiera się on na
imp
module, który stał się przestarzały od wersji 3.4, która została odrzucona na korzyśćimportlib
. (Czy to też jest możliweimportlib
? Nie wiem.) Na razie hack działa.Przykład uzyskiwania dostępu do członków
moduleX
wsubpackage1
ze skryptu znajdującego się wsubpackage2
folderze:Wydaje się, że bardziej przejrzystym podejściem jest zmodyfikowanie sys.path używanego do ładowania modułów, o czym wspomniał Federico.
źródło
__name__
zmienia się w zależności od tego, czy dany kod jest uruchamiany w globalnej przestrzeni nazw, czy jako część importowanego modułu.Jeśli kod nie działa w przestrzeni globalnej,
__name__
będzie to nazwa modułu. Jeśli jest uruchomiony w globalnej przestrzeni nazw - na przykład, jeśli wpiszesz go w konsoli lub uruchom moduł jako skrypt używającypython.exe yourscriptnamehere.py
wtedy, to__name__
staje się"__main__"
.Zobaczysz dużo kodu Pythona
if __name__ == '__main__'
używanego do testowania, czy kod jest uruchamiany z globalnej przestrzeni nazw - co pozwala mieć moduł, który może również pełnić funkcję skryptu.Czy próbowałeś wykonać te importy z konsoli?
źródło
Odpowiedź BrenBarn mówi wszystko, ale jeśli jesteś podobny do mnie, zrozumienie może trochę potrwać. Oto moja sprawa i jak odpowiedź @ BrenBarn ma do niej zastosowanie, być może pomoże ci.
Walizka
Korzystając z naszego znanego przykładu i dodaj do niego, że moduleX.py ma względny import do ..moduleA. Biorąc pod uwagę, że próbowałem napisać skrypt testowy w katalogu subpackage1, który zaimportował moduł X, ale potem dostałem przerażający błąd opisany przez OP.
Rozwiązanie
Przenieś skrypt testowy na ten sam poziom co pakiet i zaimportuj pakiet.subpackage1.moduleX
Wyjaśnienie
Jak wyjaśniono, import względny jest dokonywany względem bieżącej nazwy. Kiedy mój skrypt testowy importuje moduł X z tego samego katalogu, nazwa modułu wewnątrz modułu X to moduł X. Gdy napotka import względny, interpreter nie może wykonać kopii zapasowej hierarchii pakietów, ponieważ jest już na górze
Kiedy importuję moduł X z góry, wówczas nazwa wewnątrz modułu X to pakiet.subpackage1.moduleX i można znaleźć import względny
źródło
Napisałem mały pakiet Pythona do PyPi, który może pomóc widzom w tym pytaniu. Pakiet działa jako obejście, jeśli ktoś chce mieć możliwość uruchamiania plików Pythona zawierających importy zawierające pakiety wyższego poziomu z pakietu / projektu, nie będąc bezpośrednio w katalogu pliku importu. https://pypi.org/project/import-anywhere/
źródło
Aby Python nie wrócił do mnie „Podjęto próbę importu względnego w pakiecie innym niż pakiet”. pakiet/
init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py
Ten błąd występuje tylko wtedy, gdy stosujesz import względny do pliku nadrzędnego. Na przykład plik nadrzędny już zwraca main po wpisaniu „print ( name )” w moduleA.py. Więc ten plik jest już głównynie może zwrócić żadnego pakietu nadrzędnego dalej. względne importy są wymagane w plikach pakietów subpackage1 i subpackage2, można użyć „..”, aby odnieść się do katalogu nadrzędnego lub modułu. Ale rodzic jest, jeśli już pakiet najwyższego poziomu nie może przejść dalej niż ten katalog nadrzędny (pakiet). Takie pliki, w których stosujesz import względny do rodziców, mogą działać tylko z zastosowaniem importu bezwzględnego. Jeśli użyjesz ABSOLUTNEGO IMPORTU W PAKIETIE RODZICIELSKIM, NIE BĘDZIE BŁĄD, ponieważ python wie, kto jest na najwyższym poziomie pakietu, nawet jeśli twój plik znajduje się w paczkach z powodu koncepcji PYTHON PATH, która definiuje najwyższy poziom projektu
źródło