Zaimportować plik z podkatalogu?

455

Mam plik o nazwie tester.py, znajduje się na /project.

/projectma wywołany podkatalog libz plikiem o nazwie BoxTime.py:

/project/tester.py
/project/lib/BoxTime.py

Chcę importować BoxTimez tester. Próbowałem tego:

import lib.BoxTime

Co spowodowało:

Traceback (most recent call last):
  File "./tester.py", line 3, in <module>
    import lib.BoxTime
ImportError: No module named lib.BoxTime

Wszelkie pomysły na importowanie BoxTimez podkatalogu?

EDYTOWAĆ

__init__.pyBył problem, ale nie zapomnij, aby zapoznać się BoxTimejak lib.BoxTime, lub zastosowanie:

import lib.BoxTime as BT
...
BT.bt_function()
Adam Matan
źródło

Odpowiedzi:

536

Zajrzyj do dokumentacji pakietów (sekcja 6.4) tutaj: http://docs.python.org/tutorial/modules.html

Krótko mówiąc, musisz umieścić pusty plik o nazwie

__init__.py

w katalogu „lib”.

Greg
źródło
59
Dlaczego to jest hacky ? W ten sposób python oznacza bezpieczne / dostępne katalogi importu.
IAbstract
7
Nie tylko oznacza bezpieczne / dostępne katalogi importu, ale także umożliwia uruchomienie kodu inicjującego podczas importowania nazwy katalogu.
Sadjad
32
Tak, jest to hacking, a nawet brudne, i moim zdaniem język nie powinien narzucać sposobu ładowania plików przez system plików. W PHP rozwiązaliśmy problem, pozwalając kodowi użytkownika zarejestrować wiele funkcji automatycznego ładowania, które są wywoływane, gdy brakuje przestrzeni nazw / klasy. Następnie społeczność opracowała standard PSR-4, a Composer go implementuje, a dziś nikt nie musi się o to martwić. I nie ma głupich __init__plików zakodowanych na stałe (ale jeśli chcesz, po prostu zarejestruj hak do automatycznego ładowania! To jest różnica między hacky a hackable ).
Morgan Touverey Quilling,
4
@ AurélienOomsimport sys, os; sys.path.insert(0, os.path.abspath('..')); from sibling_package.hacks import HackyHackHack
jbowman
4
python jest bałaganiarski :)
Jimmy Pettersson
174
  • Utwórz podkatalog o nazwie lib.
  • Utwórz pusty plik o nazwie lib\__init__.py.
  • W lib\BoxTime.pynapisz taką funkcję foo():

    def foo():
        print "foo!"
  • W kodzie klienta w powyższym katalogu libwpisz:

    from lib import BoxTime
    BoxTime.foo()
  • Uruchom kod klienta. Dostaniesz:

    foo!

Znacznie później - w systemie Linux wyglądałoby to tak:

% cd ~/tmp
% mkdir lib
% touch lib/__init__.py
% cat > lib/BoxTime.py << EOF
heredoc> def foo():
heredoc>     print "foo!"
heredoc> EOF
% tree lib
lib
├── BoxTime.py
└── __init__.py

0 directories, 2 files
% python 
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from lib import BoxTime
>>> BoxTime.foo()
foo!
Hughdbrown
źródło
2
Czy możesz podać link do dokumentacji Pythona, w której to wyjaśniono? Dzięki!
Zenon
5
Niech ten link będzie klikalny: docs.python.org/3/tutorial/modules.html#packages
Gabriel Staples
Miły przewodnik dotyczący wdrażania pakietulib
MasterControlProgram
Uwaga: podkatalogi nie mogą zawierać myślników ani kropek, ale podkreślenia są ważne. dla mnie to wydaje się takie same ograniczenia, jak w przypadku innych nazw symboli, ale jeszcze go nie wykopałem do poziomu dokumentacji.
Alexander Stohr
podkreślenia => python3 (za późno na edycję komentarza)
Alexander Stohr
68

Możesz spróbować wstawić go do sys.path:

sys.path.insert(0, './lib')
import BoxTime
Kresimir
źródło
11
Jest to świetne, jeśli z jakiegoś powodu nie możesz lub nie chcesz utworzyć pliku .py init .
jpihl
1
Działa, jeśli uruchomisz Pythona z katalogu „project”. „.” jest interpretowany względem bieżącego katalogu roboczego, a nie względem katalogu, w którym znajduje się wykonywany plik. Powiedzieć ci cd /data, python ../project/tester.py. To nie zadziała.
morningstar
2
To zadziałało dla mnie. Wolę to niż plik inicjujący .py, co zapewnia czystsze instrukcje importowania.
Taylor Evanson,
5
Działa O DUŻO lepiej i jest to „prawidłowe” rozwiązanie. init .py robi bałagan w pakietach takich jak boto, które mają własne foldery podrzędne z modułami.
Dave Dopson
1
@jpihl Musisz utworzyć (przynajmniej) plik empy o nazwie __init__.py, aby umożliwić importowanie modułów Pythona z tego folderu. Wypróbowałem to rozwiązanie i działa idealnie (v2.7.6).
m3nda
31

Piszę to, ponieważ wszyscy sugerują, że musisz utworzyć libkatalog.

Nie musisz nazywać swojego podkatalogu lib. Możesz go nazwać, anythingpod warunkiem, że __init__.pygo umieścisz .

Możesz to zrobić, wprowadzając następujące polecenie w powłoce linux:

$ touch anything/__init__.py 

Więc teraz masz tę strukturę:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

Następnie możesz zaimportować mylibw main.pynastępujący sposób:

from anything import mylib 

mylib.myfun()

Możesz także zaimportować funkcje i klasy w następujący sposób:

from anything.mylib import MyClass
from anything.mylib import myfun

instance = MyClass()
result = myfun()

__init__.pyDostęp do dowolnej funkcji zmiennej lub klasy, którą umieścisz w środku, można również uzyskać:

import anything

print(anything.myvar)

Lub tak:

from anything import myvar

print(myvar)
nurettin
źródło
Moja struktura folderów to utils\__init__.pyi utils\myfile.py. (Narzędzia zawierają oba pliki) W ten sposób próbuję zaimportować from utils.myfile import myMethod. Ale rozumiem ModuleNotFoundError: No module named 'utils'. Co może być nie tak? PS: Używam Djangoi próbuję zaimportować, w views.pyktórym jest na tym samym poziomie co utilsfolder
Jagruti
Podczas importowania modułów można używać ścieżek bezwzględnych i uruchamiać program za pomocąPYTHONPATH=. python path/to/program.py
nurettin
21

Czy twój katalog lib zawiera __init__.pyplik?

Python używa __init__.pydo określenia, czy katalog jest modułem.

Przebrnąć
źródło
16

Spróbować import .lib.BoxTime. Aby uzyskać więcej informacji, przeczytaj o względnym imporcie w PEP 328 .

drrlvn
źródło
2
Nie sądzę, żebym kiedykolwiek widział tę składnię używaną wcześniej. Czy istnieje uzasadniony (nie) powód, aby użyć tej metody?
tgray
2
Dlaczego nie była to odpowiedź. Jasne, jeśli chcesz zrobić całą paczkę, powinieneś to zrobić. Ale to nie było pierwotne pytanie.
Travis Griggs,
To daje mi: ValueError: Podjęto próbę importu względnego w pakiecie
Alex
5
Działa to tylko wtedy, gdy plik, z którego importujesz, sam jest częścią pakietu. Jeśli nie, pojawi się błąd wskazany przez @Alex.
Jonathon Reinhart
8

Robię to, które zasadniczo obejmuje wszystkie przypadki (upewnij się, że masz __init__.pyw katalogu względnym / path / to / your / lib /):

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/your/lib/folder")
import someFileNameWhichIsInTheFolder
...
somefile.foo()


Przykład:
Masz w folderze projektu:

/root/myproject/app.py

Masz w innym folderze projektu:

/root/anotherproject/utils.py
/root/anotherproject/__init__.py

Chcesz użyć /root/anotherproject/utils.pyi wywołać funkcję foo, która jest w nim.

Więc piszesz w app.py:

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../anotherproject")
import utils

utils.foo()
Rtęć
źródło
2
jeśli używasz os.path, prawdopodobnie chcesz użyć os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')zamiast zakodować „/” w konkatenacji ścieżki.
cowbert
Dlaczego nie możesz po prostu obejść się "../anotherproject"bez os.path.dirname()?
Moshe Rabaev,
@MosheRabaev - Dobrą praktyką jest używanie funkcji os.path. W przypadku wpisania „../anotherproject” i przeniesienia kodu do systemu operacyjnego Windows kod się zepsuje! os.path utils wie, jak zwrócić poprawną ścieżkę, biorąc pod uwagę system operacyjny, na którym działa kod. Aby uzyskać więcej informacji docs.python.org/2/library/os.path.html
Mercury
@MosheRabaev, a jeśli użyjesz „..” bez dirname(realpath(__file__)), oblicza ścieżkę względem bieżącego katalogu roboczego po uruchomieniu skryptu, a nie względem miejsca, w którym skrypt się znajduje.
TJ Ellis,
5

Utwórz pusty plik __init__.pyw podkatalogu / lib. I dodaj na początku głównego kodu

from __future__ import absolute_import 

następnie

import lib.BoxTime as BT
...
BT.bt_function()

albo lepiej

from lib.BoxTime import bt_function
...
bt_function()
Mik
źródło
0

Tylko dodatek do tych odpowiedzi.

Jeśli chcesz zaimportować wszystkie pliki ze wszystkich podkatalogów , możesz dodać to do katalogu głównego pliku.

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

Następnie możesz po prostu zaimportować pliki z podkatalogów, tak jakby te pliki znajdowały się w bieżącym katalogu.

Przykład roboczy

Jeśli w moim projekcie mam następujący katalog z podkatalogami ...

.
├── a.py
├── b.py
├── c.py
├── subdirectory_a
   ├── d.py
   └── e.py
├── subdirectory_b
   └── f.py
├── subdirectory_c
   └── g.py
└── subdirectory_d
    └── h.py

Mogę umieścić następujący kod w moim a.pypliku

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

# And then you can import files just as if these files are inside the current directory

import b
import c
import d
import e
import f
import g
import h

Innymi słowy, kod ten wyodrębni z którego katalogu pochodzi plik.

Zwycięzca
źródło
-1

/project/tester.py

/project/lib/BoxTime.py

utwórz pusty plik __init__.pywzdłuż linii, aż dojdziesz do pliku

/project/lib/somefolder/BoxTime.py

#lib- __init__.pyneed #somefolderma dwa elementy jeden, a katalog o nazwie somefolder zawiera dwa elementy boxtime.pyi__init__.py

Chaitanya Gk
źródło
-3

Spróbuj tego:

from lib import BoxTime

Orane
źródło
8
bez żadnego wyjaśnienia nie jest to bardzo przydatne.
Jean-François Fabre