Importowanie plików z innego folderu

1402

Mam następującą strukturę folderów.

application/app/folder/file.py

i chcę zaimportować niektóre funkcje z file.py do innego pliku Python, który znajduje się w

application/app2/some_folder/some_file.py

próbowałem

from application.app.folder.file import func_name

i kilka innych prób, ale jak dotąd nie udało mi się poprawnie zaimportować. W jaki sposób mogę to zrobić?

Ivan
źródło
Czytanie oficjalnej dokumentacji bardzo mi pomogło! docs.python.org/3/reference/…
Kavin Raju S

Odpowiedzi:

1437

Domyślnie nie możesz. Podczas importowania pliku Python przeszukuje tylko bieżący katalog, katalog, z którego działa skrypt punktu wejścia, i sys.pathktóry zawiera lokalizacje, takie jak katalog instalacyjny pakietu (w rzeczywistości jest nieco bardziej złożony niż ten, ale obejmuje większość przypadków) .

Możesz jednak dodać do ścieżki Pythona w czasie wykonywania:

# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')

import file
Cameron
źródło
355
sys.path.append('/path/to/application/app/folder')jest czystszy imo
pseudosudo
386
@pseudosudo: Tak, ale wstawienie go na początku ma tę zaletę, że gwarantuje przeszukanie ścieżki przed innymi (nawet wbudowanymi) w przypadku konfliktu nazw.
Cameron
8
@kreativitea - sys.pathzwraca list, a nie deque, i to byłoby głupie, aby przekonwertować listDo dequeiz powrotem.
ArtOfWarfare
37
Czy jest to uważane za pythonowy sposób zarządzania plikami .py w folderach? Zastanawiam się ... dlaczego nie jest domyślnie obsługiwany? nie ma sensu utrzymywać wszystkich plików .py w jednym katalogu.
Ofir
49
@Ofir: Nie, to nie jest ładne, czyste rozwiązanie pythonowe. Ogólnie powinieneś używać pakietów (które są oparte na drzewach katalogów). Ta odpowiedź była specyficzna dla zadanego pytania iz jakiegoś powodu nadal cieszy się dużą popularnością.
Cameron,
708

Nic złego w:

from application.app.folder.file import func_name

Tylko upewnij się, że folderzawiera również __init__.py, pozwala to na dołączenie go jako pakietu. Nie jestem pewien, o czym mówią inne odpowiedzi PYTHONPATH.

joey
źródło
47
Ponieważ nie obejmuje to przypadków, w których PYTHONPATHkonieczna jest modyfikacja . Załóżmy, że masz dwa foldery na tym samym poziomie: Ai B. Aposiada __init.py__. Spróbuj zaimportować coś z Bwewnątrz A.
msvalkon
32
Co znajduje się w pliku init.pylub __init__.py?
Lorem Ipsum Dolor
47
@Xinyang Może to być pusty plik. Jego istnienie każe Pythonowi traktować katalog jako pakiet.
sójka
9
Oczywiście ta odpowiedź zakłada, że applicationi appsą już paczkami (tzn. Masz już __init__.pyje obie). W wyniku dodając __init__.pytakże folder, application.app.folderstaje się (pod) pakiet , z którego można uzyskać dostęp do modułu application.app.folder.file , którego symbolem func_namemogą być teraz importowane
Yibo Yang
29
Cokolwiek spróbuję, to nie zadziała. Chcę zaimportować z katalogu „rodzeństwo”, więc jeden w górę jeden w dół. Wszystkie mają __ init __. Py, w tym rodzic. Czy ten python 3 jest specyficzny?
dasWesen
109

Gdy moduły znajdują się w równoległych lokalizacjach, jak w pytaniu:

application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py

Ten skrót powoduje, że jeden moduł jest widoczny dla drugiego:

import sys
sys.path.append('../')
slizb
źródło
24
Jako zastrzeżenie: działa tak długo, jak skrypt importujący jest uruchamiany z katalogu zawierającego. W przeciwnym razie katalog nadrzędny dowolnego katalogu, z którego uruchamiany jest skrypt, zostanie dołączony do ścieżki i import zakończy się niepowodzeniem.
Carl Smith
14
Aby tego uniknąć, możemy uzyskać katalog nadrzędny plikusys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul,
To nie działało dla mnie - musiałem dodać tam dodatkową nazwę katalogu, aby wspiąć się z powrotem do rodzica, aby bieg cli/foo.pyz linii poleceń był w stanieimport cli.bar
RCross
2
@Rahul, twoje rozwiązanie nie działa dla interaktywnych powłok
towi_parallelism
Jeśli uruchomisz go z folderu głównego (tj. Folderu aplikacji), prawdopodobnie nie masz nic sys.path.append('.')przeciwko importowaniu modułu za pomocą from app2.some_folder.some_file import your_function. Alternatywnie to, co działa dla mnie, to uruchamianie python3 -m app2.another_folder.another_filez folderu głównego.
uzależniony
68

Najpierw zaimportuj sys w name-file.py

 import sys

Drugi dołączyć ścieżkę folderu w name-file.py

sys.path.insert(0, '/the/folder/path/name-package/')

Po trzecie Stwórz pusty plik o nazwie __inicjat __.py w podkatalogu (to mówi Pythonowi, że jest to pakiet)

  • nazwa-plik.py
  • pakiet nazw
    • __ init __.py
    • name-module.py

Po czwarte zaimportuj moduł do folderu w name-file.py

from name-package import name-module
Alex Montoya
źródło
5
Ponieważ folder nazw znajduje się tuż pod nazwą plik-pliku.py, powinno to działać nawet bez polecenia sys.path.insert-command. W związku z tym odpowiedź pozostawia pytanie, czy to rozwiązanie działa nawet wtedy, gdy folder nazw znajduje się w dowolnej lokalizacji.
Bastian
55

Myślę, że ad-hoc może polegać na użyciu zmiennej środowiskowej,PYTHONPATH jak opisano w dokumentacji: Python2 , Python3

# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
Ax3l
źródło
Zaraz, czy zastąpiłbym mój skrypt skryptem nazwą pliku?
Vladimir Putin
4
nie, ze ścieżką katalogu do pliku .py
Ax3l
1
Niestety, jeśli używasz Anacondy, to nie zadziała, ponieważ pod maską PYTHONPATH nie jest tak naprawdę używany wewnętrznie!
information_interchange
Aby zapoznać się z (ostatnimi) zmianami w anaconda, zobacz SO dla przepływów pracy i komentarzy do obejść: stackoverflow.com/questions/17386880/... Ogólnie mówiąc, buduj i instaluj małe pakiety zamiast hakować katalogi importu.
Ax3l
45

Odpowiedzi tutaj są niejasne, jest to testowane na Pythonie 3.6

Dzięki tej strukturze folderów:

main.py
|
---- myfolder/myfile.py

Gdzie myfile.pyjest treść:

def myfunc():
    print('hello')

Instrukcja importu w main.py:

from myfolder.myfile import myfunc
myfunc()

a to wydrukuje cześć .

danday74
źródło
9
dodanie pliku konfiguracyjnego init .py (pustego) w moim folderze działało dla mnie na Linuksie (y)
Vincent
7
@Vincent miałeś na myśli __init__.py?
mrgloom 27.04.2018
2
@mrgloom rzeczywiście
Vincent
3
Z jakiegoś powodu dodawanie __init__.pynie działa dla mnie. Używam Py 3.6.5 na Ubuntu 18. Działa na Pycharm, ale nie z terminala
Crearo Rotar
9
Jest to całkowicie niezwiązane z pytaniem, które dotyczy importowania plików z innej gałęzi drzewa plików niż bieżący katalog roboczy.
Alexander Rossa
44

Problem polega na tym, że Python szuka tego pliku w katalogu Python i go nie znajduje. Musisz określić, że mówisz o katalogu, w którym się znajdujesz, a nie o języku Python.

Aby to zrobić, zmień to:

from application.app.folder.file import func_name

do tego:

from .application.app.folder.file import func_name

Dodając kropkę, mówisz, spójrz w tym folderze na folder aplikacji zamiast szukać w katalogu Python.

CianB
źródło
2
właśnie to mnie naprawiło
Phi
32

Z tego co wiem, dodaj __init__.pyplik bezpośrednio w folderze funkcji, które chcesz zaimportować, wykona zadanie.

Vaibhav Singh
źródło
7
tylko jeśli skrypt, który chce dołączyć ten inny katalog, znajduje się już w sys.path
Ax3l
2
Użyłem sys.path.append(tools_dir)na Windowsie i nie muszę dodawać __init__.py' file in my directory katalogu tools_dir`
herve-guerin
25

W Pythonie 3.4 i nowszych wersjach można importować bezpośrednio z pliku źródłowego (link do dokumentacji) . To nie jest najprostsze rozwiązanie, ale dołączam tę odpowiedź dla kompletności.

Oto przykład. Najpierw plik do zaimportowania o nazwie foo.py:

def announce():
    print("Imported!")

Kod, który importuje powyższy plik, w dużej mierze zainspirowany przykładem w dokumentacji:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

foo = module_from_file("foo", "/path/to/foo.py")

if __name__ == "__main__":
    print(foo)
    print(dir(foo))
    foo.announce()

Wyjście:

<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Zauważ, że nazwa zmiennej, nazwa modułu i nazwa pliku nie muszą być zgodne. Ten kod nadal działa:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

baz = module_from_file("bar", "/path/to/foo.py")

if __name__ == "__main__":
    print(baz)
    print(dir(baz))
    baz.announce()

Wyjście:

<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Programowe importowanie modułów zostało wprowadzone w Pythonie 3.1 i daje większą kontrolę nad sposobem importowania modułów. Więcej informacji znajduje się w dokumentacji.

wecsam
źródło
11
Nie wiem, czy ktokolwiek próbował to zrozumieć, ale myślę, że to zbyt skomplikowane.
Dan
23

Pracował dla mnie w python3 na Linuksie

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  
dsg38
źródło
6
sys.path.append("/home/linux/folder/")- Upewnij się, że nie korzystasz ze skrótu, np."~/folder/"
daGo
22

Wypróbuj import względny Pythona:

from ...app.folder.file import func_name

Każda kropka wiodąca to kolejny wyższy poziom w hierarchii, rozpoczynający się od bieżącego katalogu.


Problemy? Jeśli to nie działa, prawdopodobnie masz problem z relatywnym importem wielu gotcha. Przeczytaj odpowiedzi i komentarze, aby uzyskać więcej informacji: Jak naprawić „Próba importu względnego w pakiecie innym”, nawet jeśli __init__.py

Wskazówka: mieć __init__.pyna każdym poziomie katalogu. Możesz potrzebować python -m application.app2.some_folder.some_file(pomijając .py), który uruchamiasz z katalogu najwyższego poziomu lub masz ten katalog najwyższego poziomu w PYTHONPATH. Uff!

Zectbumo
źródło
22

Stoiłem przed tym samym wyzwaniem, zwłaszcza podczas importowania wielu plików, w ten sposób udało mi się go pokonać.

import os, sys

from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))

from root_folder import file_name
Erick Mwazonga
źródło
2
Odpowiedź byłaby bardziej pomocna, gdybyś mógł wyjaśnić, co robi inaczej niż zwykły import?
not2qubit
1
Miałem /path/dir1/__init__.py i /path/dir1/mod.py. W przypadku /path/some.py z dir1.mod import func działał. Gdy w /path/dir2/some.py zadziałało to dopiero po skopiowaniu i wklejeniu powyższej odpowiedzi na początku pliku. Nie chciałem edytować mojej ścieżki, ponieważ nie każdy projekt Pythona, który mam, znajduje się w / path /.
jwal
19

Biorąc pod uwagę, applicationjako głównego katalogu swojego projektu Pythona, utwórz pusty __init__.pyplik application, appi folderfoldery. Następnie some_file.pydokonaj zmian w następujący sposób, aby uzyskać definicję func_name:

import sys
sys.path.insert(0, r'/from/root/directory/application')

from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()
ChandanK
źródło
powinno być: sys.path.insert (0, r '/ from / root / directory')
Bolaka,
18

Użycie sys.path.append z bezwzględną ścieżką nie jest idealne przy przenoszeniu aplikacji do innych środowisk. Użycie ścieżki względnej nie zawsze będzie działać, ponieważ bieżący katalog roboczy zależy od sposobu wywołania skryptu.

Ponieważ struktura folderów aplikacji jest stała, możemy użyć os.path, aby uzyskać pełną ścieżkę modułu, który chcemy zaimportować. Na przykład, jeśli jest to struktura:

/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py

Powiedzmy, że chcesz zaimportować moduł „mango”. W vanilla.py możesz wykonać następujące czynności:

import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango

Oczywiście nie potrzebujesz zmiennej mango_dir.

Aby zrozumieć, jak to działa, spójrz na ten przykład sesji interaktywnej:

>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
    '/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>> 
>>> newdir
'/home/me/application/app2/another_folder'
>>> 

I sprawdź dokumentację os.path .

Nagev
źródło
13

To działa dla mnie w systemie Windows

# some_file.py on mainApp/app2 
import sys
sys.path.insert(0, sys.path[0]+'\\app2')

import some_file
Eeeus
źródło
10

Jestem wyjątkowy: używam Pythona w systemie Windows!

Po prostu uzupełniam informacje: zarówno w systemie Windows, jak i Linux działają ścieżki względne i bezwzględne sys.path(potrzebuję ścieżek względnych, ponieważ używam skryptów na kilku komputerach i w różnych katalogach głównych).

A kiedy zarówno w systemie Windows \i /może być używany jako separator dla nazw plików i oczywiście trzeba podwoić \do strun Pythonie
niektórych ważnych przykładów:

sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')

(uwaga: myślę, że /jest to wygodniejsze niż \zdarzenie, jeśli jest mniej „natywne dla systemu Windows”, ponieważ jest kompatybilne z Linuksem i łatwiejsze do pisania i kopiowania do Eksploratora Windows)

herve-guerin
źródło
4
os.path.join ('tools', 'mydir')
Corey Goldberg
7

Jeśli celem ładowania modułu z określonej ścieżki jest udzielenie pomocy podczas tworzenia modułu niestandardowego, można utworzyć łącze symboliczne w tym samym folderze skryptu testowego, który wskazuje na katalog główny modułu niestandardowego. To odwołanie do modułu będzie miało pierwszeństwo przed innymi modułami zainstalowanymi o tej samej nazwie dla dowolnego skryptu uruchomionego w tym folderze.

Testowałem to na Linuksie, ale powinno działać w każdym nowoczesnym systemie operacyjnym obsługującym łącza symboliczne.

Zaletą tego podejścia jest to, że możesz wskazać moduł, który siedzi we własnej kopii roboczej lokalnego oddziału SVC, co może znacznie uprościć czas cyklu programowania i zmniejszyć tryby awarii zarządzania różnymi wersjami modułu.

Timothy C. Quinn
źródło
7

W moim przypadku miałem klasę do zaimportowania. Mój plik wyglądał tak:

# /opt/path/to/code/log_helper.py
class LogHelper:
    # stuff here

W moim głównym pliku umieściłem kod poprzez:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper
schmudu
źródło
1
@ not2qubit sys nie został zaimportowany w odpowiedzi.
Walter
6

Kilka razy wpadłem na to samo pytanie, więc chciałbym podzielić się moim rozwiązaniem.

Wersja Python: 3.X

Poniższe rozwiązanie jest przeznaczone dla osób, które opracowują aplikację w języku Python w wersji 3.X, ponieważ język Python 2 nie jest obsługiwany od 1 stycznia 2020 r .

Struktura projektu

W Pythonie 3 nie potrzebujesz __init__.pyw podkatalogu projektu ze względu na pakiety Implicit Namespace . Zobacz Czy init .py nie jest wymagany dla pakietów w Python 3.3+

Project 
├── main.py
├── .gitignore
|
├── a
|   └── file_a.py
|
└── b
    └── file_b.py

Opis problemu

W file_b.pychciałbym zaimportować klasę Aw file_a.pyfolderze a.

Rozwiązania

# 1 Szybki, ale brudny sposób

Bez instalowania pakietu, tak jak obecnie tworzysz nowy projekt

Za pomocą przycisku try catchsprawdź, czy błędy. Przykład kodu:

import sys
try:
    # The insertion index should be 1 because index 0 is this file
    sys.path.insert(1, '/absolute/path/to/folder/a')  # the type of path is string
    # because the system path already have the absolute path to folder a
    # so it can recognize file_a.py while searching 
    from file_a import A
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

# 2 Zainstaluj pakiet

Po zainstalowaniu aplikacji (w tym poście samouczek instalacji nie jest dołączony)

Możesz po prostu

try:
    from __future__ import absolute_import
    # now it can reach class A of file_a.py in folder a 
    # by relative import
    from ..a.file_a import A  
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

Miłego kodowania!

WY Hsu
źródło
aby uzyskać więcej informacji na temat importu bezwzględnego
WY Hsu
3

Pracowałem nad projektem, aktóry chciałbym, aby użytkownicy zainstalowali za pip install apomocą następującej listy plików:

.
├── setup.py
├── MANIFEST.in
└── a
    ├── __init__.py
    ├── a.py
    └── b
        ├── __init__.py
        └── b.py

setup.py

from setuptools import setup

setup (
  name='a',
  version='0.0.1',
  packages=['a'],
  package_data={
    'a': ['b/*'],
  },
)

MANIFEST.in

recursive-include b *.*

a / init .py

from __future__ import absolute_import

from a.a import cats
import a.b

a / a.py

cats = 0

a / b / init .py

from __future__ import absolute_import

from a.b.b import dogs

a / b / b.py

dogs = 1

Zainstalowałem moduł, uruchamiając następujące z katalogu z MANIFEST.in:

python setup.py install

Następnie z zupełnie innej lokalizacji w moim systemie plików /moustache/armwrestlemogłem uruchomić:

import a
dir(a)

Co potwierdziło, że a.catsrzeczywiście równa się 0 i a.b.dogsrzeczywiście równa się 1, zgodnie z zamierzeniami.

duhaime
źródło
2

Zamiast po prostu import ...wykonaj:

from <MySubFolder> import <MyFile>

MyFile znajduje się w MySubFolder.

Galileoomega
źródło
1

Możesz odświeżyć powłokę Pythona, naciskając klawisz f5 lub przejdź do opcji Run-> Uruchom moduł. W ten sposób nie musisz zmieniać katalogu, aby odczytać coś z pliku. Python automatycznie zmieni katalog. Ale jeśli chcesz pracować z różnymi plikami z innego katalogu w Pythonie Shell, możesz zmienić katalog w sys, jak wcześniej powiedział Cameron .

IOstream
źródło
1

Właśnie kliknąłem moje IDE prawym przyciskiem myszy i dodałem nowy folderi zastanawiałem się, dlaczego nie mogłem z niego importować. Później zrozumiałem, że muszę kliknąć prawym przyciskiem myszy i utworzyć pakiet Python, a nie klasyczny folder systemu plików. Lub metoda pośmiertna polegająca na dodaniu __init__.py(co powoduje, że Python traktuje folder systemu plików jako pakiet), jak wspomniano w innych odpowiedziach. Dodając tutaj tę odpowiedź na wypadek, gdyby ktoś wybrał tę trasę.

mithunpaul
źródło
1

Możesz użyć importlib, aby zaimportować moduły, w których chcesz zaimportować moduł z folderu za pomocą łańcucha:

import importlib

scriptName = 'Snake'

script = importlib.import_module('Scripts\\.%s' % scriptName)

W tym przykładzie znajduje się main.py, który jest powyższym kodem, a następnie folder o nazwie Skrypty, a następnie można wywoływać z tego folderu, co jest potrzebne, zmieniając scriptNamezmienną. Następnie możesz użyć scriptodniesienia do tego modułu. na przykład jeśli mam funkcję wywoływaną Hello()w module Snake, możesz uruchomić tę funkcję, wykonując to:

script.Hello()

Przetestowałem to w Pythonie 3.6

Dextron
źródło
0

Miałem te problemy wiele razy. Często odwiedzam tę samą stronę. W moim ostatnim problemie musiałem uruchomić serverze stałego katalogu, ale za każdym razem, gdy debugowałem, chciałem uruchomić z różnych podkatalogów.

import sys
sys.insert(1, /path) 

zrobił NIE działa dla mnie, ponieważ w różnych modułach musiałem przeczytać inny * .csv plików, które były w tym samym katalogu.

W końcu to, co zadziałało, nie było pytoniczne, ale:

Użyłem if __main__ na górze modułu, który chciałem debugować , który jest uruchamiany z innej niż zwykle ścieżki.

Więc:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
B Furtado
źródło