Importare un file da una sottodirectory?


456

Ho un file chiamato tester.py, situato su /project.

/projectha una sottodirectory chiamata lib, con un file chiamato BoxTime.py:

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

Voglio importare BoxTimeda tester. Ho provato questo:

import lib.BoxTime

Quale risultato:

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

Qualche idea su come importare BoxTimedalla sottodirectory?

MODIFICARE

L' __init__.pyera il problema, ma non dimenticare di fare riferimento a BoxTimecome lib.BoxTime, o l'uso:

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

Risposte:


536

Dai un'occhiata alla documentazione dei pacchetti (Sezione 6.4) qui: http://docs.python.org/tutorial/modules.html

In breve, è necessario inserire un file vuoto denominato

__init__.py

nella directory "lib".


59
Perché si sente confuso ? È il modo in cui Python contrassegna directory di importazione sicure / disponibili.
Estratto del

7
Non solo contrassegna le directory di importazione sicure / disponibili, ma fornisce anche un modo per eseguire un codice di inizializzazione durante l'importazione di un nome di directory.
Sadjad,

32
Sì, questo è confuso e persino sporco, e secondo me la lingua non dovrebbe imporre il suo modo di caricare file attraverso il filesystem. In PHP abbiamo risolto il problema lasciando che il codice utenteland registrasse più funzioni di caricamento automatico che vengono chiamate quando manca uno spazio dei nomi / classe. Quindi la community ha prodotto lo standard PSR-4 e Composer lo implementa e al giorno d'oggi nessuno deve preoccuparsene. E niente stupidi __init__file hardcoded (ma se lo vuoi, registra semplicemente un hook di caricamento automatico! Questa è la differenza tra hacky e 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 è disordinato :)
Jimmy Pettersson,

174
  • Crea una sottodirectory denominata lib.
  • Crea un file vuoto chiamato lib\__init__.py.
  • In lib\BoxTime.py, scrivi una funzione foo()come questa:

    def foo():
        print "foo!"
  • Nel codice client nella directory sopra lib, scrivi:

    from lib import BoxTime
    BoxTime.foo()
  • Esegui il tuo codice cliente. Otterrete:

    foo!

Molto più tardi - in Linux, sarebbe simile a questo:

% 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
Potresti fornire un link alla documentazione di Python in cui questo è spiegato? Grazie!
Zenon,


Bella guida per l'implementazione di un pacchettolib
MasterControlProgram

nota: i sottocartelle non devono contenere trattini o punti, ma i caratteri di sottolineatura sono validi. per me sembra le stesse restrizioni degli altri nomi di simboli, ma non l'ho ancora scavato a livello di documentazione.
Alexander Stohr,

underscores => python3 (troppo tardi per modificare il commento)
Alexander Stohr,

68

Puoi provare a inserirlo in sys.path:

sys.path.insert(0, './lib')
import BoxTime

11
Questo è ottimo se per qualche motivo non puoi o non vuoi creare il file init .py.
Jpihl

1
Funziona se si esegue python dalla directory "project". Il "." viene interpretato in relazione alla directory di lavoro corrente, non in relazione alla directory in cui risiede il file in esecuzione. Di ' cd /data, python ../project/tester.py. Quindi non funzionerà.
morningstar,

2
Questo ha funzionato per me. Preferisco questo su un file init .py, rende dichiarazioni di importazione più pulite.
Taylor Evanson,

5
Funziona MOLTO meglio ed è la soluzione "corretta". init .py rovina i pacchetti come boto che hanno le loro cartelle secondarie con moduli.
Dave Dopson,

1
@jpihl Devi creare (almeno) un file empy chiamato __init__.py per consentire moduli di importazione python da quella cartella. Ho provato quella soluzione e funziona perfettamente (v2.7.6).
m3nda,

31

Sto scrivendo questo perché tutti sembrano suggerire che è necessario creare una libdirectory.

Non è necessario assegnare un nome alla sottodirectory lib. Puoi nominarlo a anythingcondizione che tu ne inserisca uno __init__.py.

Puoi farlo inserendo il seguente comando in una shell linux:

$ touch anything/__init__.py 

Quindi ora hai questa struttura:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

Quindi puoi importare mylibin main.pyquesto modo:

from anything import mylib 

mylib.myfun()

Puoi anche importare funzioni e classi come questa:

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

instance = MyClass()
result = myfun()

È __init__.pypossibile accedere a qualsiasi funzione o classe variabile inserita all'interno :

import anything

print(anything.myvar)

O così:

from anything import myvar

print(myvar)

La mia struttura di cartelle è utils\__init__.pye utils\myfile.py. (Le utilità contengono entrambi i file) Ecco come sto cercando di importare from utils.myfile import myMethod. Ma capisco ModuleNotFoundError: No module named 'utils'. Cosa potrebbe esserci di sbagliato? PS: sto usando Djangoe sto cercando di importare in views.pyuno stesso livello della utilscartella
Jagruti

È possibile utilizzare percorsi assoluti durante l'importazione di moduli ed eseguire il programma conPYTHONPATH=. python path/to/program.py
nurettin

21

La tua directory lib contiene un __init__.pyfile?

Python usa __init__.pyper determinare se una directory è un modulo.


16

Prova import .lib.BoxTime. Per ulteriori informazioni leggi sull'importazione relativa in PEP 328 .


2
Non credo di aver mai visto quella sintassi usata prima. C'è un motivo valido (non) per usare questo metodo?
tgray,

2
Perché non è stata questa la risposta. Certo, se vuoi fare l'intera cosa dei pacchetti, dovresti farlo. Ma non è questa la domanda originale.
Travis Griggs,

Questo mi dà: ValueError: Tentativo di importazione relativa in non pacchetto
Alex

5
Funziona solo se il file da cui stai importando fa parte di esso stesso di un pacchetto. In caso contrario, riceverai l'errore segnalato da @Alex.
Jonathon Reinhart,

8

Faccio questo che sostanzialmente copre tutti i casi (assicurati di avere __init__.pynella cartella relativa / 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()


Esempio:
hai nella cartella del progetto:

/root/myproject/app.py

Hai in un'altra cartella del progetto:

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

Si desidera utilizzare /root/anotherproject/utils.pye chiamare la funzione foo che è in esso.

Quindi scrivi in ​​app.py:

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

utils.foo()

2
se stai usando os.pathprobabilmente vorrai usare os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')invece di hardcoding il '/' nella tua concatenazione di percorso.
Cowbert,

Perché non puoi semplicemente fare a "../anotherproject"meno del os.path.dirname()?
Moshe Rabaev,

@MosheRabaev - È buona norma utilizzare le funzioni os.path. In caso di scrivere "../anotherproject" e spostare il codice sul sistema operativo Windows, il codice si romperà! os.path utils sa come restituire il percorso corretto considerando il sistema operativo su cui è in esecuzione il codice. per maggiori informazioni docs.python.org/2/library/os.path.html
Mercurio

@MosheRabaev e se si utilizza ".." senza il dirname(realpath(__file__)), quindi calcolerà il percorso relativo alla directory di lavoro corrente quando si esegue lo script, non rispetto a dove si trova lo script.
TJ Ellis,

5

Crea un file vuoto __init__.pynella sottodirectory / lib. E aggiungi all'inizio del codice principale

from __future__ import absolute_import 

poi

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

o meglio

from lib.BoxTime import bt_function
...
bt_function()

0

Solo un'aggiunta a queste risposte.

Se vuoi importare tutti i file da tutte le sottodirectory , puoi aggiungerlo alla radice del tuo file.

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

E quindi puoi semplicemente importare file dalle sottodirectory proprio come se questi file si trovassero nella directory corrente.

Esempio funzionante

Se ho la seguente directory con le sottodirectory nel mio progetto ...

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

Posso inserire il seguente codice nel mio a.pyfile

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

In altre parole, questo codice verrà estratto dalla directory da cui proviene il file.


-1

/project/tester.py

/project/lib/BoxTime.py

crea un file vuoto __init__.pylungo la linea fino a raggiungere il file

/project/lib/somefolder/BoxTime.py

#lib- needs ha due elementi uno __init__.pye una directory chiamata somefolder #somefolderha due elementi boxtime.pye__init__.py


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.