Mengimpor file dari subdirektori?

456

Saya memiliki file bernama tester.py, terletak di /project.

/projectmemiliki subdirektori bernama lib, dengan file bernama BoxTime.py:

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

Saya ingin mengimpor BoxTimedari tester. Saya sudah mencoba ini:

import lib.BoxTime

Yang menghasilkan:

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

Adakah ide cara mengimpor BoxTimedari subdirektori?

EDIT

The __init__.pyadalah masalah, tapi jangan lupa untuk merujuk BoxTimesebagai lib.BoxTime, atau menggunakan:

import lib.BoxTime as BT
...
BT.bt_function()
Adam Matan
sumber

Jawaban:

536

Lihatlah dokumentasi Paket (Bagian 6.4) di sini: http://docs.python.org/tutorial/modules.html

Singkatnya, Anda harus meletakkan file kosong bernama

__init__.py

di direktori "lib".

Greg
sumber
59
Kenapa rasanya dirusak ? Ini adalah cara python menandai direktori impor aman / tersedia.
IAbstract
7
Tidak hanya menandai direktori impor aman / tersedia, tetapi juga menyediakan cara untuk menjalankan beberapa kode inisialisasi saat mengimpor nama direktori.
Sadjad
32
Ya ini hacky dan bahkan kotor, dan menurut saya bahasa tidak boleh memaksakan cara memuat file di sistem file. Dalam PHP kami memecahkan masalah dengan membiarkan kode userland mendaftarkan beberapa fungsi autoloading yang dipanggil ketika namespace / kelas hilang. Kemudian masyarakat telah menghasilkan standar PSR-4 dan Komposer mengimplementasikannya, dan saat ini tidak ada yang perlu khawatir tentang itu. Dan tidak ada __init__file hardcode bodoh (tetapi jika Anda menginginkannya, cukup daftarkan hook autoloading! Inilah perbedaan antara hacky dan 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 itu berantakan :)
Jimmy Pettersson
174
  • Buat subdirektori bernama lib.
  • Buat file kosong bernama lib\__init__.py.
  • Di lib\BoxTime.py, tulis fungsi foo()seperti ini:

    def foo():
        print "foo!"
    
  • Dalam kode klien Anda di direktori di atas lib, tulis:

    from lib import BoxTime
    BoxTime.foo()
    
  • Jalankan kode klien Anda. Kamu akan mendapatkan:

    foo!

Jauh kemudian - di linux, akan terlihat seperti ini:

% 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
sumber
2
Bisakah Anda memberikan tautan ke dokumentasi Python di mana ini dijelaskan? Terima kasih!
Zenon
5
Mari kita buat tautan itu dapat diklik: docs.python.org/3/tutorial/modules.html#packages
Gabriel Staples
Panduan yang bagus untuk mengimplementasikan sebuah paketlib
MasterControlProgram
harap dicatat: subdir tidak boleh mengandung tanda hubung atau titik, tetapi garis bawah valid. bagi saya yang sepertinya pembatasan yang sama dengan nama simbol lainnya, tetapi saya belum menggali ke tingkat dokumentasi.
Alexander Stohr
underscores => python3 (terlambat mengedit komentar)
Alexander Stohr
68

Anda dapat mencoba memasukkannya ke sys.path:

sys.path.insert(0, './lib')
import BoxTime
Kresimir
sumber
11
Ini bagus jika Anda karena suatu alasan tidak dapat atau tidak akan membuat file .py init .
jpihl
1
Ini berfungsi jika Anda menjalankan python dari direktori "proyek". "." ditafsirkan relatif terhadap direktori kerja Anda saat ini, bukan relatif terhadap direktori tempat file yang Anda jalankan hidup. Katakanlah Anda cd /data, python ../project/tester.py. Maka itu tidak akan berhasil.
morningstar
2
Ini berhasil untuk saya. Saya lebih suka ini daripada file init .py, ini membuat pernyataan impor lebih bersih.
Taylor Evanson
5
Ini bekerja JAUH lebih baik dan merupakan solusi "benar". init .py mengacaukan paket seperti boto yang memiliki folder anak sendiri dengan modul.
Dave Dopson
1
@ jpihl Anda harus membuat (setidaknya) file empy bernama __init__.py untuk mengizinkan modul impor python dari folder itu. Saya sudah mencoba solusi itu dan bekerja dengan sempurna (v2.7.6).
m3nda
31

Saya menulis ini karena semua orang tampaknya menyarankan Anda harus membuat libdirektori.

Anda tidak perlu memberi nama sub-direktori Anda lib. Anda dapat memberi nama anythingasalkan Anda memasukkannya __init__.pyke dalamnya.

Anda dapat melakukannya dengan memasukkan perintah berikut di shell linux:

$ touch anything/__init__.py 

Jadi sekarang Anda memiliki struktur ini:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

Maka Anda dapat mengimpor mylibke main.pyseperti ini:

from anything import mylib 

mylib.myfun()

Anda juga dapat mengimpor fungsi dan kelas seperti ini:

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

instance = MyClass()
result = myfun()

Fungsi variabel atau kelas yang Anda tempatkan di dalam __init__.pyjuga dapat diakses:

import anything

print(anything.myvar)

Atau seperti ini:

from anything import myvar

print(myvar)
nurettin
sumber
Struktur folder saya adalah utils\__init__.pydan utils\myfile.py. (Utils mengandung kedua file) Ini adalah cara saya mencoba mengimpor from utils.myfile import myMethod. Tapi saya mengerti ModuleNotFoundError: No module named 'utils'. Apa yang salah? PS: Saya menggunakan Djangodan mencoba untuk mengimpor views.pyyang pada tingkat yang sama dengan utilsfolder
Jagruti
Dimungkinkan untuk menggunakan jalur absolut saat mengimpor modul dan menjalankan program Anda denganPYTHONPATH=. python path/to/program.py
nurettin
21

Apakah direktori lib Anda mengandung __init__.pyfile?

Python digunakan __init__.pyuntuk menentukan apakah direktori adalah modul.

Menyeberang
sumber
16

Coba import .lib.BoxTime. Untuk informasi lebih lanjut baca tentang impor relatif di PEP 328 .

drrlvn
sumber
2
Saya tidak berpikir saya pernah melihat sintaks yang digunakan sebelumnya. Apakah ada alasan kuat (tidak) untuk menggunakan metode ini?
tgray
2
Kenapa bukan ini jawabannya. Tentu, jika Anda ingin melakukan seluruh paket, Anda harus melakukannya. Tapi bukan itu pertanyaan aslinya.
Travis Griggs
Ini memberi saya: ValueError: Upaya impor relatif non-paket
Alex
5
Ini hanya berfungsi jika file yang Anda impor itu sendiri merupakan bagian dari paket. Jika tidak, Anda akan menerima kesalahan yang ditunjukkan @Alex.
Jonathon Reinhart
8

Saya melakukan ini yang pada dasarnya mencakup semua kasus (pastikan Anda memiliki __init__.pydi folder relatif / path / ke / Anda / lib /):

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


Contoh:
Anda ada di folder proyek Anda:

/root/myproject/app.py

Anda ada di folder proyek lain:

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

Anda ingin menggunakan /root/anotherproject/utils.pydan memanggil fungsi foo yang ada di dalamnya.

Jadi, Anda menulis di app.py:

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

utils.foo()
Air raksa
sumber
2
jika Anda menggunakan, os.pathAnda mungkin ingin menggunakan os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')alih-alih hardcoding '/' dalam rangkaian path Anda.
cowbert
Mengapa Anda tidak bisa melakukannya "../anotherproject"tanpa os.path.dirname()?
Moshe Rabaev
@ MosheRabaev - Ini adalah praktik yang baik untuk menggunakan fungsi os.path. Jika ada tulisan "../anproject" dan pindahkan kodenya ke OS Windows, kodenya akan rusak! os.path utils tahu cara mengembalikan jalur yang benar mengingat OS yang menjalankan kode. untuk info lebih lanjut docs.python.org/2/library/os.path.html
Mercury
@ MosheRabaev dan jika Anda menggunakan ".." tanpa dirname(realpath(__file__)), maka itu akan menghitung jalur relatif ke direktori kerja Anda saat ini ketika Anda menjalankan skrip, bukan relatif terhadap tempat skrip tinggal.
TJ Ellis
5

Buat file kosong __init__.pydi subdirektori / lib. Dan tambahkan di awal kode utama

from __future__ import absolute_import 

kemudian

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

atau lebih baik

from lib.BoxTime import bt_function
...
bt_function()
Mik
sumber
0

Hanya tambahan untuk jawaban ini.

Jika Anda ingin mengimpor semua file dari semua subdirektori , Anda dapat menambahkan ini ke root file Anda.

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

Dan kemudian Anda cukup mengimpor file dari subdirektori sama seperti jika file-file ini ada di dalam direktori saat ini.

Contoh kerja

Jika saya memiliki direktori berikut dengan subdirektori di proyek saya ...

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

Saya dapat memasukkan kode berikut di dalam a.pyfile saya

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

Dengan kata lain, kode ini akan abstrak dari direktori mana file itu berasal.

Pemenang
sumber
-1

/project/tester.py

/project/lib/BoxTime.py

buat file kosong __init__.pydi baris sampai Anda mencapai file

/project/lib/somefolder/BoxTime.py

#lib- Kebutuhan memiliki dua item satu __init__.pydan direktori bernama somefolder #somefoldermemiliki dua item boxtime.pydan__init__.py

Chaitanya Gk
sumber
-3

coba ini:

from lib import BoxTime

Orane
sumber
8
tanpa penjelasan apa pun ini tidak terlalu berguna.
Jean-François Fabre