2013-05-19 27 views
47

mam katalogu:względna importu Pythona 3 nie pracuje

mydirectory 
├── __init__.py 
├── file1.py 
└── file2.py 

mam funkcji F zdefiniowane w file1.py.

Jeżeli w file2.py, robię

from .file1 import f 

pojawia się następujący błąd:

SystemError: Parent module '' not loaded, cannot perform relative import

Dlaczego? I jak to działa?

+0

Czy używasz 'file2.py' bezpośrednio? – Blender

+0

Tak, robię: 'python3 file2.py' z poziomu wiersza poleceń –

+18

Jeśli moduł Pythona jest częścią pakietu, * nie powinieneś * uruchamiać go jako głównego. Jeśli rozpowszechnisz swoją bibliotekę, pakiety przejdą do 'site-packages', ale skrypty powinny przejść do'/usr/bin' lub czegoś podobnego (stąd potrzeba bezwzględnego importu). Powinno istnieć wyraźne rozróżnienie między modułem Pythona, który został napisany do wykonania, a modułem zapisanym jako część biblioteki. – Bakuriu

Odpowiedz

24

od file1 i file2 są w tym samym katalogu, nie trzeba nawet mieć pliku __init__.py. Jeśli masz zamiar się skalować, zostaw to.

Aby importować coś w pliku w tym samym katalogu, po prostu nie podoba

from file1 import f

to znaczy, że nie trzeba robić ścieżkę względną .file1 ponieważ są one w tym samym katalogu.

Jeśli twoja główna funkcja, skrypt lub cokolwiek innego, że będzie działała cała aplikacja znajduje się w innym katalogu, będziesz musiał zrobić wszystko względem tego, gdzie jest wykonywany.

+5

Zakłada się, że nie ma innego 'file1' w PYTHONPATH. – Jens

19

Po uruchomieniu pliku źródłowego Pythona nie można importować innego pliku, który znajduje się w bieżącym pakiecie, przy użyciu importu względnego.

W documentation jest powiedziane:

Zauważ, że import względne są na podstawie nazwy bieżącego modułu. Ponieważ nazwa modułu głównego jest zawsze "__main__", moduły przeznaczone do użycia jako główny moduł aplikacji Python muszą zawsze używać bezwzględnych importów.

Tak więc, jak powiedział @mrKelley, należy użyć bezwzględnego importu w takiej sytuacji.

0
myproject/ 

mypackage 
├── __init__.py 
├── file1.py 
├── file2.py 
└── file3.py 

mymainscript.py 

przykład importu z jednego pliku do innego

#file1.py 
from myproject import file2 
from myproject.file3 import MyClass 

importu przykład opakowania do mainscript

#mymainscript.py 
import mypackage 

https://docs.python.org/3/tutorial/modules.html#packages

https://docs.python.org/3/reference/import.html#regular-packages

https://docs.python.org/3/reference/simple_stmts.html#the-import-statement

https://docs.python.org/3/glossary.html#term-import-path

Zmienna sys.path jest listą ciągów, który określa ścieżkę przeszukiwania interpreter dla modułów. Jest inicjalizowany do domyślnej ścieżki pobranej ze zmiennej środowiskowej PYTHONPATH lub z wbudowanej wartości domyślnej, jeśli PYTHONPATH nie jest ustawiona.Można modyfikować za pomocą standardowych operacji listy:

import sys 
sys.path.append('/ufs/guido/lib/python') 
sys.path.insert(0, '/ufs/guido/myhaxxlib/python') 

Umieszczenie go na początku ma tę zaletę, jaką jest zagwarantowanie, że ścieżka jest przeszukiwana przed innymi (nawet wbudowanych w nich) w przypadku konfliktów nazw.

35

Uruchamianie modułów wewnątrz paczki w postaci plików wykonywalnych to zła praktyka .

Kiedy coś tworzysz, budujesz bibliotekę, która ma być importowana przez inne programy, a zatem nie ma sensu pozwalać na bezpośrednie wykonywanie jej submodułów lub budujesz plik wykonywalny, w którym to przypadku nie ma powodu aby uczynić go częścią pakietu.

To dlatego w setup.py można rozróżnić pakiety i skrypty. Pakiety zostaną objęte pod site-packages, a skrypty zostaną zainstalowane pod /usr/bin (lub podobnym miejscem w zależności od systemu operacyjnego).

Moja rekomendacja jest więc używać następujący układ:

/ 
├── mydirectory 
| ├── __init__.py 
| ├── file1.py 
└── file2.py 

Gdzie file2.py import file1.py jak każdy inny kod, który chce korzystać z biblioteki mydirectory, wybitnych absolutnego importu:

from mydirectory.file1 import f 

Podczas pisania skryptu setup.py dla projektu wystarczy wpisać mydirectory jako pakiet i file2.py jako scenariusz i wszystko będzie działać. Nie musisz się bawić z sys.path.

Jeśli kiedykolwiek, z jakiegoś powodu, naprawdę chcesz faktycznie uruchomić podmodułu pakietu, właściwy sposób to zrobić jest użycie przełącznika -m:

python -m mydirectory.file1 

Ten ładuje cały pakiet, a następnie wykonuje moduł jako skrypt, umożliwiając powodzenie względnego importu.

Osobiście uniknę tego. Również dlatego, że wiele osób nawet nie wie, że możesz to zrobić i skończy się otrzymaniem tego samego błędu co Ty i myślisz, że pakiet jest zepsuty.


chodzi aktualnie akceptowanych odpowiedź, która mówi, że należy po prostu użyć niejawna względną importu from file1 import f bo to będzie działać, ponieważ są one w tym samym katalogu:

To źle!

  • Będzie nie praca w python3 gdzie ukryte import względne są niedozwolone i będą na pewno przerwać, jeśli zdarzy ci się mieć zainstalowany moduł file1 (ponieważ zostanie on importowany zamiast modułu!).
  • Nawet jeśli zadziała, file1 nie będzie widoczny jako część pakietu mydirectory. Ten numer może być ważny pod numerem.

    Na przykład, jeśli file1 używa pickle, nazwa pakietu jest ważna dla prawidłowego załadowania/rozładowania danych.

+1

Czy jest to również zła praktyka, jeśli masz plik testu jednostkowego w tym samym katalogu i chcesz uruchomić ten plik testowy jednostki? – gsanta

+0

@gsanta Dlaczego chciałbyś umieścić tam łatki? Typowa struktura projektu zawiera katalog testowy lub testowy zawierający wszystkie pliki testowe, a rzeczywiste źródła znajdują się w innym katalogu. – Bakuriu

+0

Pochodzę ze świata javascript, w którym rośnie tendencja do umieszczania pliku testu jednostki obok pliku, który chcesz przetestować, i uważam, że jest to dobra praktyka. Ale może nie jest tak popularny w programowaniu Pythona. (A czasami chciałbym uruchomić tylko ten plik testowy, nie wszystkie pliki testów jednostkowych, kiedy chcę szybko sprawdzić, czy działa) – gsanta