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()

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”.


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!

2
Czy możesz podać link do dokumentacji Pythona, w której to wyjaśniono? Dzięki!
Zenon


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

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)

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.


16

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


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()

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()

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.


-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


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.