2010-11-03 10 views
10

Nie wiedziałem, jak poprawnie nazwać to pytanie.Priorytet importowania w języku Python: pakiety lub moduły?

Przypadek 1

Załóżmy, że mam następującą strukturę katalogów.

foo 
| 
+- bar/__init__.py 
| 
+- bar.py 

Jeśli mam

from foo import bar 

Jak mam wiedzieć, który bar (bar.py lub bar/__init__.py) jest importowany? Czy istnieje prosty sposób automatycznego wykrycia tego?

Case 2

foo 
| 
+- foo.py 
| 
+- other.py 

Jeśli other.py ma linię

import foo 

Skąd mam wiedzieć, który Foo (foo lub foo.foo) jest importowany? Znowu, czy istnieje łatwy sposób automatycznego wykrycia tego?

+2

łatwy sposób go wykryć: najpierw linia bar.py za być 'print (" bar .py import ")' – nmichaels

+2

Powinienem mieć RTFM-ed. http://docs.python.org/tutorial/modules.html to świetny zasób. –

+0

Czy istnieje sposób zmuszenia programu do użycia jednej lub drugiej interpretacji? – Sobi

Odpowiedz

6

TLDR; pakiet ma pierwszeństwo przed modułem o tej samej nazwie, jeśli znajdują się w tym samym katalogu.

Od docs:

„Gdy moduł o nazwie spam jest importowany, wyszukuje interpreter dla pliku o nazwie spam.py w bieżącym katalogu, a następnie na liście katalogów określonych przez zmienną środowiskową PYTHONPATH. Ma taką samą składnię jak zmienna PATH, czyli lista nazw katalogów. "

ten jest nieco mylący, ponieważ interpreter będzie też spojrzeć na pakiet o nazwie spam (katalog o nazwie spam zawierający plik __init__.py). Ponieważ pozycje katalogu są sortowane przed przeszukiwaniem, pakiety mają pierwszeństwo przed modułami o tej samej nazwie, jeśli znajdują się w tym samym katalogu, ponieważ spam występuje przed spam.py.

Należy zwrócić uwagę, że "katalog bieżący" odnosi się do głównej ścieżki skryptu (tej, w której __name__ == '__main__' is True). Więc jeśli jesteś na /home/billg nazywając /foo/bar.py, "bieżący katalog" odnosi się do /foo.

+1

Czy coś się zmieniło? ponieważ nie wydaje mi się, aby dodać bieżący katalog, jak sugerujesz. Dokumenty teraz stwierdzają: "Kiedy moduł o nazwie spam jest importowany, interpreter najpierw wyszukuje wbudowany moduł o tej nazwie.Jeśli nie zostanie znaleziony, to wyszukuje plik o nazwie spam.py w liście katalogów podanych przez zmienna sys.path. " I nie widzę, żeby "bieżący katalog" był wymieniony. Ale odkąd jestem zagubiony, może nie rozumiem? – PatrickT

+1

I (mógłbym być nieporozumieniem z tego wszystkiego) poprzednia odpowiedź stackoverflow stwierdza: "Python nie dodaje bieżącego katalogu do sys.path, ale raczej do katalogu, w którym znajduje się skrypt." http://stackoverflow.com/questions/2325923/how-to-fix-importerror-no-module-named-error-in-python – PatrickT

+0

Nie wydaje się, aby odpowiedzieć ** Przypadek 1 ** – qff

0

w pierwszym przypadku próbujesz importować pasek funkcji z pliku „skrypt foo.py”

W drugim, który próbujesz zaimportować plik „skrypt foo.py”

2

opakowań (katalogi z __init__.py) mają pierwszeństwo przed modułami. Dokumentacja tego faktu jest trudna do znalezienia, ale można to zobaczyć w źródle: python 2.7, python 3.6 (dzięki @qff za znalezienie).

Będziesz także potrzebować __init__.py w katalogu foo, aby Twój przykład zadziałał.

Jeśli other.py jest wewnątrz foo/ to będzie załadować foo.py (a nie katalog foo/), ponieważ będzie to wyglądać w bieżącym katalogu pierwszy (chyba że grałeś z PYTHONPATH lub sys.path).

+0

Skąd wiadomo, że ma pierwszeństwo? - Nie mogłem go znaleźć w [dokumentacji Pythona] (https://docs.python.org/2/tutorial/modules.html) – qff

+0

@ qff Testowałem to. Testowałem także python3 właśnie teraz i ma to samo zachowanie. Link do oficjalnej dokumentacji tego faktu byłby dobry. Jeśli ją znajdziesz, możesz edytować moją odpowiedź lub opublikować komentarz, a ja go edytuję. – kanaka

+0

Znalazłem! (rodzaj) - wpisy w katalogu są sortowane przed próbą załadowania każdego z nich jako pakietu lub modułu. Zapewnia to, że pakiety są ładowane jako pierwsze. [Link do kodu źródłowego CPython] (https://github.com/python/cpython/blob/c30098c8c6014f3340a369a31df9c74bdbacc269/Lib/pkgutil.py#L235) – qff

5

z powłoki Pythona:

from foo import bar 

print bar.__file__ 

powinien powiedzieć, który plik został przywieziony

Roba

+0

nie atrybut __file__ na python3 – gabn88

Powiązane problemy