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.pyw wierszu poleceń. Jest ładowany jako moduł, jeśli tak python -m myfile, lub jeśli jest ładowany, gdy importinstrukcja 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:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
jeśli zaimportowałeś moduleX(uwaga: zaimportowany , a nie bezpośrednio wykonany), jego nazwą byłoby package.subpackage1.moduleX. Jeśli zaimportujesz moduleA, jego nazwa to package.moduleA. Jeśli jednak uruchomisz bezpośrednio moduleX z wiersza polecenia, jego nazwa będzie zamiast tego __main__, a jeśli bezpośrednio uruchomisz moduleAz 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/subpackage1a następnie zrobisz import moduleX, nazwa po moduleXprostu będzie moduleX, a nie package.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 pythoni 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ę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.
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 to package.subpackage1.moduleX, to ..moduleAznaczy package.moduleA. Aby a from .. importdziałał, nazwa modułu musi zawierać co najmniej tyle kropek, ile jest w importinstrukcji.
... 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 .. importinstrukcji. 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 moduleXitp. 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ć moduleXbezpośrednio, ale nadal chcesz, aby był traktowany jako część pakietu, możesz to zrobić python -m package.subpackage1.moduleX. -mMó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, powiedzmy myfile.py, który używa funkcji w środku moduleX. W takim przypadku umieść myfile.py gdzie indziej - nie w packagekatalogu - i uruchom go. Jeśli w środku myfile.pyrobisz takie rzeczy from package.moduleA import spam, będzie dobrze.
Notatki
Dla każdego z tych rozwiązań katalog pakietu ( packagew 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__tak None.)