Mengimpor file dari folder berbeda

1407

Saya memiliki struktur folder berikut.

application/app/folder/file.py

dan saya ingin mengimpor beberapa fungsi dari file.py di file Python lain yang berada di

application/app2/some_folder/some_file.py

Saya sudah mencoba

from application.app.folder.file import func_name

dan beberapa upaya lainnya tetapi sejauh ini saya tidak dapat mengimpor dengan benar. Bagaimana saya bisa melakukan ini?

Ivan
sumber
Membaca dokumentasi resmi sangat membantu saya! docs.python.org/3/reference/…
Kavin Raju S

Jawaban:

1445

Secara default, Anda tidak bisa. Saat mengimpor file, Python hanya mencari direktori saat ini, direktori tempat skrip entry-point dijalankan, dan sys.pathyang mencakup lokasi seperti direktori instalasi paket (sebenarnya sedikit lebih kompleks dari ini, tetapi ini mencakup sebagian besar kasus) .

Namun, Anda dapat menambahkan ke jalur Python saat runtime:

# 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
sumber
356
sys.path.append('/path/to/application/app/folder')is cleaner imo
pseudosudo
387
@ pseudosudo: Ya, benar, tetapi memasukkannya di awal memiliki manfaat untuk memastikan bahwa jalur dicari sebelum yang lain (bahkan yang terpasang) dalam kasus penamaan konflik.
Cameron
8
@kreativitea - sys.pathmengembalikan a list, bukan a deque, dan itu konyol untuk mengonversikannya listmenjadi dequedan kembali.
ArtOfWarfare
37
Apakah ini dianggap sebagai cara pythonic untuk mengelola file .py di folder? Saya bertanya-tanya ... mengapa tidak didukung secara default? tidak masuk akal untuk mempertahankan semua file .py dalam satu direktori ..
Ofir
49
@Ofir: Tidak, ini bukan solusi pythonic bersih yang bagus. Secara umum, Anda harus menggunakan paket (yang didasarkan pada pohon direktori). Jawaban ini khusus untuk pertanyaan yang diajukan, dan untuk beberapa alasan terus bertambah jumlah besar upvotes.
Cameron
709

Tidak ada yang salah dengan:

from application.app.folder.file import func_name

Pastikan folderjuga mengandung __init__.py, ini memungkinkan untuk dimasukkan sebagai paket. Tidak yakin mengapa jawaban lain dibicarakan PYTHONPATH.

joey
sumber
47
Karena ini tidak mencakup kasus-kasus di mana modifikasi PYTHONPATHdiperlukan. Katakanlah Anda memiliki dua folder pada level yang sama: Adan B. Amemiliki __init.py__. Coba impor sesuatu dari Bdalam A.
msvalkon
32
Apa yang ada di dalam init.pyatau __init__.pyfile?
Lorem Ipsum Dolor
48
@ Xinyang Ini bisa menjadi file kosong. Keberadaannya memberitahu Python untuk memperlakukan direktori sebagai sebuah paket.
jay
9
Tentu saja jawaban ini mengasumsikan bahwa applicationdan appadalah paket sudah (yaitu Anda sudah memiliki __init__.pydi keduanya). Sebagai hasil dari penambahan __init__.pyjuga menjadi folder, application.app.folder(sub) paket , dari mana Anda dapat mengakses modul application.app.folder.file , yang simbolnya func_namesekarang dapat diimpor
Yibo Yang
30
Apa pun yang saya coba, ini tidak akan berhasil. Saya ingin mengimpor dari direktori "saudara", jadi satu naik satu turun. Semua memiliki __ init __. Py, termasuk orang tua. Apakah python 3 ini khusus?
dasWesen
111

Ketika modul berada di lokasi paralel, seperti dalam pertanyaan:

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

Singkatan ini membuat satu modul terlihat oleh yang lain:

import sys
sys.path.append('../')
slizb
sumber
24
Sebagai peringatan: Ini berfungsi selama skrip pengimporan dijalankan dari direktori yang berisi. Kalau tidak, direktori induk dari direktori lain mana pun skrip dijalankan akan ditambahkan ke path dan impor akan gagal.
Carl Smith
14
Untuk menghindari itu, kita bisa mendapatkan direktori induk dari filesys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul
Itu tidak berhasil bagi saya - saya harus menambahkan dirname tambahan di sana untuk naik kembali ke induk, sehingga berlari cli/foo.pydari baris perintah dapatimport cli.bar
RCross
2
@Rahul, solusi Anda tidak berfungsi untuk shell interaktif
towi_parallelism
Jika Anda menjalankannya dari folder root (mis. Folder aplikasi), Anda mungkin baik-baik saja dengan sys.path.append('.')mengimpor modul dengan menggunakan from app2.some_folder.some_file import your_function. Atau yang berfungsi untuk saya adalah menjalankan python3 -m app2.another_folder.another_filedari folder root.
kecanduan
68

Pertama sys impor di name-file.py

 import sys

Kedua tambahkan path folder di name-file.py

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

Ketiga, buat file kosong bernama __ init __.py di subdirektori Anda (ini memberi tahu Python itu paket)

  • name-file.py
  • nama-paket
    • __ init __.py
    • name-module.py

Impor keempat modul di dalam folder di name-file.py

from name-package import name-module
Alex Montoya
sumber
5
Dengan nama-folder berada tepat di bawah name-file.py, ini akan berfungsi bahkan tanpa sys.path.insertperintah-. Dengan demikian, jawabannya meninggalkan pertanyaan, apakah solusi ini berfungsi bahkan ketika folder nama berada di lokasi yang arbitrer.
Bastian
55

Saya pikir cara ad-hoc akan menggunakan variabel lingkunganPYTHONPATH seperti yang dijelaskan dalam dokumentasi: Python2 , Python3

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

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
Ax3l
sumber
Tunggu, apakah saya akan mengganti skrip saya dengan nama file?
Vladimir Putin
4
tidak, dengan jalur direktori ke file .py Anda
Ax3l
1
Sayangnya, jika Anda menggunakan Anaconda, ini tidak akan berfungsi, karena di bawah kap PYTHONPATH tidak benar-benar digunakan secara internal!
information_interchange
Untuk perubahan (baru-baru ini) di anaconda, lihat SO ini untuk alur kerja dan komentar untuk penyelesaian: stackoverflow.com/questions/17386880/... Secara umum, buat dan instal paket kecil alih-alih meretas direktori impor.
Ax3l
47

Jawaban di sini kurang jelas, ini diuji pada Python 3.6

Dengan struktur folder ini:

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

Di mana myfile.pymemiliki konten:

def myfunc():
    print('hello')

Pernyataan impor dalam main.pyadalah:

from myfolder.myfile import myfunc
myfunc()

dan ini akan mencetak halo .

danday74
sumber
9
menambahkan init .py (kosong) file konfigurasi di myfolder bekerja untuk saya di linux (y)
Vincent
7
@Vincent maksudmu __init__.py?
mrgloom
2
@mrgloom memang
Vincent
3
Untuk beberapa alasan, menambahkan __init__.pytidak berfungsi untuk saya. Saya menggunakan Py 3.6.5 di Ubuntu 18. Ini bekerja pada Pycharm tetapi tidak dari terminal
Crearo Rotar
9
Ini sama sekali tidak terkait dengan pertanyaan yang menanyakan tentang mengimpor file dari cabang berbeda dari pohon file dari direktori kerja saat ini.
Alexander Rossa
45

Masalah Anda adalah bahwa Python mencari di direktori Python untuk file ini dan tidak menemukannya. Anda harus menentukan bahwa Anda berbicara tentang direktori tempat Anda berada dan bukan direktori Python.

Untuk melakukan ini, Anda ubah ini:

from application.app.folder.file import func_name

untuk ini:

from .application.app.folder.file import func_name

Dengan menambahkan titik Anda mengatakan lihat di folder ini untuk folder aplikasi daripada melihat di direktori Python.

CianB
sumber
2
inilah yang memperbaiki saya
Phi
32

Dari yang saya tahu, tambahkan __init__.py file langsung di folder fungsi yang ingin Anda impor akan melakukan pekerjaan.

Vaibhav Singh
sumber
7
hanya jika skrip yang ingin memasukkan direktori lain sudah ada di sys.path
Ax3l
2
Saya menggunakan sys.path.append(tools_dir)Windows dan saya tidak perlu menambahkan __init__.py' file in my directory tools_dir`
herve-guerin
25

Dalam Python 3.4 dan yang lebih baru, Anda dapat mengimpor dari file sumber secara langsung (tautan ke dokumentasi) . Ini bukan solusi paling sederhana, tapi saya memasukkan jawaban ini untuk kelengkapan.

Berikut ini sebuah contoh. Pertama, file yang akan diimpor, bernama foo.py:

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

Kode yang mengimpor file di atas, sangat terinspirasi oleh contoh dalam dokumentasi:

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

Hasil:

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

Perhatikan bahwa nama variabel, nama modul, dan nama file tidak perlu cocok. Kode ini masih berfungsi:

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

Hasil:

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

Modul yang diimpor secara program diperkenalkan dengan Python 3.1 dan memberi Anda lebih banyak kontrol atas bagaimana modul diimpor. Lihat dokumentasi untuk informasi lebih lanjut.

wecsam
sumber
11
Saya tidak tahu apakah ada yang mencoba memahami ini, tetapi saya pikir itu terlalu rumit.
Dan
23

Bekerja untuk saya di python3 di linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  
dsg38
sumber
6
sys.path.append("/home/linux/folder/")- Pastikan jangan menggunakan jalan pintas misalnya"~/folder/"
daGo
22

Coba impor relatif Python:

from ...app.folder.file import func_name

Setiap titik awal adalah level lain yang lebih tinggi dalam hierarki yang dimulai dengan direktori saat ini.


Masalah? Jika ini tidak bekerja untuk Anda, maka Anda mungkin mendapatkan sedikit dari banyak impor relatif gotcha. Baca jawaban dan komentar untuk detail lebih lanjut: Bagaimana cara memperbaiki "Impor relatif yang diupayakan dalam non-paket" bahkan dengan __init__.py

Petunjuk: miliki __init__.pydi setiap level direktori. Anda mungkin perlu python -m application.app2.some_folder.some_file(meninggalkan .py) yang Anda jalankan dari direktori tingkat atas atau memiliki direktori tingkat atas itu di PYTHONPATH Anda. Fiuh!

Zectbumo
sumber
22

Saya dihadapkan dengan tantangan yang sama, terutama ketika mengimpor beberapa file, ini adalah bagaimana saya berhasil mengatasinya.

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
sumber
2
Jawaban Anda akan lebih bermanfaat jika Anda bisa menjelaskan apa bedanya dengan impor biasa?
not2qubit
1
Saya memiliki /path/dir1/__init__.py dan /path/dir1/mod.py. Untuk /path/some.py dari dir1.mod fungsi impor berfungsi. Ketika di /path/dir2/some.py hanya berfungsi setelah saya menyalin dan menempelkan jawaban di atas di bagian atas file. Tidak ingin mengedit jalur saya karena tidak semua proyek python yang saya miliki ada di / path /.
jwal
19

Mempertimbangkan applicationsebagai direktori root untuk proyek python Anda, buat __init__.pyfile kosong di dalamnya application, appdan folderfolder. Kemudian di some_file.pylakukan perubahan sebagai berikut untuk mendapatkan definisi 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
sumber
seharusnya: sys.path.insert (0, r '/ dari / root / direktori')
Bolaka
18

Menggunakan sys.path.append dengan jalur absolut tidak ideal saat memindahkan aplikasi ke lingkungan lain. Menggunakan jalur relatif tidak akan selalu berhasil karena direktori kerja saat ini tergantung pada bagaimana skrip dipanggil.

Karena struktur folder aplikasi telah diperbaiki, kita dapat menggunakan os.path untuk mendapatkan path lengkap dari modul yang ingin kita impor. Misalnya, jika ini strukturnya:

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

Dan katakanlah Anda ingin mengimpor modul "mangga". Anda dapat melakukan hal berikut di vanilla.py:

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

Tentu saja, Anda tidak perlu variabel mango_dir.

Untuk memahami cara kerjanya lihat contoh sesi interaktif ini:

>>> 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'
>>> 

Dan periksa dokumentasi os.path .

Nagev
sumber
13

Ini bekerja untuk saya di windows

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

import some_file
Emeeus
sumber
10

Saya cukup istimewa: Saya menggunakan Python dengan Windows!

Saya baru saja melengkapi informasi: untuk Windows dan Linux, path relatif dan absolut berfungsi sys.path(saya perlu path relatif karena saya menggunakan skrip saya pada beberapa PC dan di bawah direktori utama yang berbeda).

Dan ketika menggunakan Windows baik \dan /dapat digunakan sebagai pemisah untuk nama file dan tentu saja Anda harus menggandakan \menjadi string Python,
beberapa contoh yang valid:

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

(catatan: Saya pikir itu /lebih nyaman daripada \, jika kurang 'Windows-asli' karena Linux-kompatibel dan lebih mudah untuk menulis dan menyalin ke Windows explorer)

herve-guerin
sumber
4
os.path.join ('tools', 'mydir')
Corey Goldberg
7

Jika tujuan memuat modul dari jalur tertentu adalah untuk membantu Anda selama pengembangan modul khusus, Anda dapat membuat tautan simbolis di folder yang sama dari skrip pengujian yang menunjuk ke akar modul khusus. Referensi modul ini akan diutamakan daripada modul lain yang diinstal dengan nama yang sama untuk setiap skrip yang dijalankan di folder itu.

Saya menguji ini di Linux tetapi harus bekerja di OS modern yang mendukung tautan simbolik.

Satu keuntungan dari pendekatan ini adalah Anda dapat menunjuk ke sebuah modul yang berada di copy kantor cabang SVC Anda sendiri yang dapat sangat menyederhanakan waktu siklus pengembangan dan mengurangi mode kegagalan mengelola berbagai versi modul.

Timothy C. Quinn
sumber
7

Dalam kasus saya, saya memiliki kelas untuk diimpor. File saya terlihat seperti ini:

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

Di file utama saya, saya memasukkan kode melalui:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper
schmudu
sumber
1
@ not2qubit sys tidak diimpor dalam jawabannya.
Walter
7

Saya bertemu pertanyaan yang sama beberapa kali, jadi saya ingin membagikan solusi saya.

Versi Python: 3.X

Solusi berikut ini untuk seseorang yang mengembangkan aplikasi Anda di Python versi 3.X karena Python 2 tidak didukung sejak Jan / 1/2020 .

Struktur Proyek

Dalam python 3, Anda tidak perlu __init__.pydalam subdirektori proyek Anda karena Paket Namespace Implisit . Lihat Apakah init .py tidak diperlukan untuk paket dalam Python 3.3+

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

Pernyataan masalah

Di file_b.py, saya ingin mengimpor kelas Adifile_a.py bawah folder a.

Solusi

# 1 Cara cepat tapi kotor

Tanpa menginstal paket seperti Anda sedang mengembangkan proyek baru

Menggunakan try catchuntuk memeriksa apakah ada kesalahan. Contoh kode:

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 Instal paket Anda

Setelah Anda menginstal aplikasi Anda (dalam posting ini, tutorial instalasi tidak termasuk)

Anda bisa saja

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

Selamat coding!

WY Hsu
sumber
untuk info lebih lanjut tentang impor absolut
WY Hsu
3

Saya sedang mengerjakan proyek ayang saya ingin pengguna instal melalui pip install adengan daftar file berikut:

.
├── 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

Saya memasang modul dengan menjalankan yang berikut dari direktori dengan MANIFEST.in:

python setup.py install

Kemudian, dari lokasi yang sama sekali berbeda pada sistem file saya, /moustache/armwrestlesaya dapat menjalankan:

import a
dir(a)

Yang mengkonfirmasi bahwa a.catsmemang sama dengan 0 dan a.b.dogsmemang sama dengan 1, sebagaimana dimaksud.

duhaime
sumber
2

Alih-alih hanya melakukan import ..., lakukan ini:

from <MySubFolder> import <MyFile>

MyFile ada di dalam MySubFolder.

Galileoomega
sumber
1

Anda dapat menyegarkan shell Python dengan menekan f5, atau pergi ke Run-> Run Module. Dengan cara ini Anda tidak perlu mengubah direktori untuk membaca sesuatu dari file. Python akan secara otomatis mengubah direktori. Tetapi jika Anda ingin bekerja dengan file yang berbeda dari direktori yang berbeda di Shell Python, maka Anda dapat mengubah direktori di sys, seperti yang dikatakan Cameron sebelumnya.

IOstream
sumber
1

Jadi saya baru saja mengklik IDE saya, dan menambahkan yang baru folderdan bertanya-tanya mengapa saya tidak dapat mengimpor darinya. Kemudian saya menyadari bahwa saya harus mengklik kanan dan membuat Paket Python, dan bukan folder sistem file klasik. Atau metode post-mortem menambahkan __init__.py(yang membuat python memperlakukan folder sistem file sebagai sebuah paket) sebagaimana disebutkan dalam jawaban lain. Menambahkan jawaban ini di sini untuk berjaga-jaga seandainya seseorang melewati rute ini.

mithunpaul
sumber
1

Anda dapat menggunakan importlib untuk mengimpor modul tempat Anda ingin mengimpor modul dari folder menggunakan string seperti ini:

import importlib

scriptName = 'Snake'

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

Contoh ini memiliki main.py yang merupakan kode di atas lalu folder yang disebut Script dan kemudian Anda dapat memanggil apa pun yang Anda butuhkan dari folder ini dengan mengubah scriptNamevariabel. Anda kemudian dapat menggunakan scriptuntuk referensi ke modul ini. seperti jika saya memiliki fungsi yang disebut Hello()dalam modul Snake Anda dapat menjalankan fungsi ini dengan melakukannya:

script.Hello()

Saya telah menguji ini dengan Python 3.6

Dextron
sumber
0

Saya mengalami masalah ini beberapa kali. Saya sudah sering mengunjungi halaman ini. Dalam masalah terakhir saya, saya harus menjalankan serverdari direktori tetap, tetapi setiap kali debug saya ingin menjalankan dari berbagai sub-direktori.

import sys
sys.insert(1, /path) 

melakukan TIDAK bekerja untuk saya karena pada modul yang berbeda saya harus membaca yang berbeda * .csv file yang semua berada di direktori yang sama.

Pada akhirnya, yang berhasil bagi saya bukanlah pythonic, saya kira, tetapi:

Saya menggunakan if __main__ modul yang ingin saya debug , yang dijalankan dari jalur yang berbeda dari biasanya.

Begitu:

# 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
sumber