Wygląda na to, że jest już sporo pytań na temat względnego importu w Pythonie 3, ale po przejściu wielu z nich nadal nie znalazłem odpowiedzi na mój problem. więc oto pytanie.
Mam pakiet pokazany poniżej
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
i mam jedną linię w test.py:
from ..A import foo
teraz jestem w folderze package
i biegam
python -m test_A.test
Dostałem wiadomość
"ValueError: attempted relative import beyond top-level package"
ale jeśli jestem w folderze nadrzędnym package
np. uruchamiam:
cd ..
python -m package.test_A.test
wszystko w porządku.
Teraz moje pytanie brzmi:
kiedy jestem w folderze package
i uruchamiam moduł w sub-pakiecie test_A, ponieważ test_A.test
, zgodnie z moim zrozumieniem, ..A
idzie tylko o jeden poziom, który wciąż znajduje się w package
folderze, dlaczego wyświetla komunikat beyond top-level package
. Jaki jest dokładnie powód, który powoduje ten komunikat o błędzie?
Odpowiedzi:
EDYCJA: Istnieją lepsze / bardziej spójne odpowiedzi na to pytanie w innych pytaniach:
Dlaczego to nie działa? Jest tak, ponieważ python nie rejestruje, skąd pakiet został załadowany. Więc kiedy to zrobisz
python -m test_A.test
, po prostu odrzuca wiedzę, któratest_A.test
jest faktycznie przechowywanapackage
(tzn.package
Nie jest uważana za pakiet). Próbafrom ..A import foo
dostępu do informacji, których już nie ma (tj. Katalogów rodzeństwa załadowanej lokalizacji). Jest to koncepcyjnie podobne do zezwalaniafrom ..os import path
na plik wmath
. Byłoby to złe, ponieważ chcesz, aby pakiety były odrębne. Jeśli muszą użyć czegoś z innego pakietu, powinni odwołać się do nich globalnie za pomocąfrom os import path
i pozwolić pythonowi sprawdzić, gdzie to jest za pomocą$PATH
i$PYTHONPATH
.Kiedy używasz
python -m package.test_A.test
, wtedy używaniefrom ..A import foo
rozwiązuje się dobrze, ponieważ śledziło to, co jest wpackage
środku, a ty po prostu uzyskujesz dostęp do katalogu potomnego załadowanej lokalizacji.Dlaczego Python nie uważa bieżącego katalogu roboczego za pakiet? Bez pojęcia , ale Boże, byłoby to użyteczne.
źródło
-m
flagi i uruchomienie z katalogu powyżej.sys.path
włamania, ale użycia setuptools , co moim zdaniem jest znacznie bardziej interesujące.Spróbuj tego. Pracował dla mnie.
źródło
A/bar.py
Istnieje i jest wfoo.py
tobiefrom .bar import X
.Założenie:
jeśli jesteś w
package
kataloguA
itest_A
jesteś oddzielnym pakietem.Wniosek:
..A
import jest dozwolony tylko w pakiecie.Dalsze uwagi:
Udostępnianie względnych importów tylko w pakietach jest przydatne, jeśli chcesz wymusić umieszczenie pakietów na dowolnej ścieżce
sys.path
.EDYTOWAĆ:
Bieżący katalog roboczy zwykle znajduje się w sys.path. Wszystkie pliki można więc importować. Jest to zachowanie od Python 2, gdy pakiety jeszcze nie istniały. Utworzenie działającego katalogu jako pakietu pozwoliłoby na import modułów jako „import .A” i „import A”, które wówczas byłyby dwoma różnymi modułami. Być może jest to niekonsekwencja do rozważenia.
źródło
python -m package.test_A.test
wydaje się robić to, co jest pożądane, a moim argumentem jest to, że powinno to być domyślne. Czy możesz mi podać przykład tej niespójności?#include
byłby bardzo przydatny!Żadne z tych rozwiązań nie działało dla mnie w wersji 3.6, ze strukturą folderów taką jak:
Moim celem było zaimportowanie z modułu 1 do modułu 2. Dziwne, co w końcu dla mnie zadziałało:
Zwróć uwagę na pojedynczą kropkę w przeciwieństwie do wspomnianych dotychczas rozwiązań z dwiema kropkami.
Edycja: Poniższe pomogło mi to wyjaśnić:
W moim przypadku katalog roboczy był (nieoczekiwanie) katalogiem głównym projektu.
źródło
sys.path.append(".")
działało, ponieważ wywołujesz go w katalogu nadrzędnym, pamiętaj, że.
zawsze reprezentuje katalog, w którym uruchamiasz komendę python.from package.A import foo
Myślę, że to jest jaśniejsze niż
źródło
sys.path.append("..")
. testowany na Pythonie 3.6Jak sugeruje najbardziej popularna odpowiedź, w zasadzie dlatego, że twoja
PYTHONPATH
lubsys.path
zawiera.
twoją ścieżkę do pakietu. A względny import jest związany z bieżącym katalogiem roboczym, a nie plikiem, w którym odbywa się import; dziwnie.Możesz to naprawić, najpierw zmieniając import względny na absolutny, a następnie zaczynając od:
LUB wymuszanie ścieżki Pythona podczas wywoływania w ten sposób, ponieważ:
Podczas
python -m test_A.test
wykonywania zatest_A/test.py
pomocą__name__ == '__main__'
i__file__ == '/absolute/path/to/test_A/test.py'
Oznacza to, że
test.py
możesz użyć swojego absolutnieimport
częściowo chronionego w głównym przypadku, a także wykonać jednorazową manipulację ścieżką w Pythonie:źródło
Edytuj: 2020-05-08: Wygląda na to, że strona, którą cytowałem, nie jest już kontrolowana przez osobę, która napisała poradę, więc usuwam link do strony. Dzięki za poinformowanie mnie o baxx.
Jeśli ktoś wciąż ma problemy z uzyskaniem już świetnych odpowiedzi, znalazłem porady na stronie, która nie jest już dostępna.
Niezbędny cytat ze strony, o której wspomniałem:
To oczywiste, że tak musi być, myśląc o tym po fakcie. Próbowałem użyć sys.path.append ('..') w moich testach, ale natknąłem się na problem opublikowany przez OP. Dodając definicję importu i sys.path przed innymi importami, udało mi się rozwiązać problem.
źródło
jeśli masz
__init__.py
w górnym folderze, możesz zainicjować import jakimport file/path as alias
w tym pliku inicjującym. Następnie możesz użyć go na niższych skryptach jako:źródło
Moim skromnym zdaniem rozumiem to pytanie w następujący sposób:
[PRZYPADEK 1] Po rozpoczęciu importu bezwzględnego, takiego jak
lub
lub
tak naprawdę ustawiasz kotwicę importu na
test_A
, innymi słowy, pakiet najwyższego poziomu totest_A
. Tak więc, gdy mamy test.pyfrom ..A import xxx
, uciekasz od kotwicy, a Python nie pozwala na to.[PRZYPADEK 2] Kiedy to zrobisz
lub
Twój kotwica staje się
package
takpackage/test_A/test.py
robićfrom ..A import xxx
nie uciec kotwicę (nadal wewnątrzpackage
folderu), a Python szczęśliwie akceptuje to.W skrócie:
Ponadto możemy użyć pełnej nazwy modułu (FQMN), aby sprawdzić ten problem.
Sprawdź FQMN w każdym przypadku:
test.__name__
=package.test_A.test
test.__name__
=test_A.test
Tak więc, w przypadku CASE2, powstanie
from .. import xxx
nowy moduł z FQMN =package.xxx
, co jest dopuszczalne.Podczas gdy w przypadku CASE1,
..
od wewnątrzfrom .. import xxx
wyskoczy z węzła początkowego (kotwicy)test_A
i NIE jest to dozwolone przez Python.źródło
Nie jestem pewien w Pythonie 2.x, ale w Pythonie 3.6, zakładając, że próbujesz uruchomić cały pakiet, wystarczy użyć
-t
Na takiej strukturze
Można na przykład użyć:
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
I nadal importuj
my_module.my_class
bez większych dramatów.źródło