Python: impor modul dari direktori lain pada level yang sama dalam hierarki proyek

89

Saya telah melihat semua jenis contoh dan pertanyaan serupa lainnya, tetapi saya tidak dapat menemukan contoh yang sama persis dengan skenario saya. Saya merasa seperti orang jahat yang menanyakan hal ini karena ada begitu banyak pertanyaan serupa, tetapi saya sepertinya tidak bisa membuat ini bekerja "dengan benar." Ini proyek saya:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |
        |------- Scripts/
        |           |
        |           |----- __init__.py
        |           |----- CreateUser.py
        |           |----- FindUser.py

Jika saya memindahkan "CreateUser.py" ke direktori utama user_management, saya dapat dengan mudah menggunakan: "import Modules.LDAPManager"untuk mengimpor LDAPManager.py --- ini berfungsi. Yang tidak dapat saya lakukan (yang ingin saya lakukan), adalah tetap membuat CreateUser.py di subfolder Scripts, dan mengimpor LDAPManager.py. Saya berharap untuk mencapai ini dengan menggunakan "import user_management.Modules.LDAPManager.py". Ini tidak berhasil. Singkatnya, saya bisa mendapatkan file Python untuk dengan mudah melihat hierarki yang lebih dalam, tetapi saya tidak bisa mendapatkan skrip Python untuk mereferensikan satu direktori dan turun ke direktori lain.

Perhatikan bahwa saya dapat menyelesaikan masalah saya menggunakan:

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import Modules.LDAPManager as LDAPManager

Saya pernah mendengar bahwa ini adalah praktik yang buruk dan berkecil hati.

File-file dalam Scripts dimaksudkan untuk dieksekusi secara langsung (apakah init .py dalam Scripts bahkan diperlukan?). Saya telah membaca bahwa dalam kasus ini, saya harus menjalankan CreateUser.py dengan bendera -m. Saya telah mencoba beberapa variasi dan sepertinya CreateUser.py tidak bisa mengenali LDAPManager.py.

CptSupermrkt
sumber

Jawaban:

67

Jika saya pindah CreateUser.pyke direktori utama user_management, saya dapat dengan mudah menggunakan: import Modules.LDAPManageruntuk mengimpor LDAPManager.py --- ini berfungsi.

Tolong, jangan . Dengan cara ini LDAPManagermodul yang digunakan oleh CreateUserakan tidak sama dengan yang diimpor melalui impor lainnya. Ini dapat menimbulkan masalah ketika Anda memiliki beberapa status global dalam modul atau selama pengawetan / pembongkaran. Menghindari impor yang hanya berfungsi karena modul berada di direktori yang sama.

Ketika Anda memiliki struktur paket, Anda harus:

  • Gunakan impor relatif, yaitu jika CreateUser.pyada di Scripts/:

     from ..Modules import LDAPManager
    

    Perhatikan bahwa ini adalah (catatan masa lalu tegang) berkecil hati dengan PEP 8 hanya karena versi lama python tidak mendukung mereka sangat baik, tapi masalah ini diselesaikan tahun yang lalu. The sekarang versi PEP 8 tidak menunjukkan mereka sebagai alternatif yang dapat diterima untuk impor mutlak. Saya sebenarnya suka mereka di dalam paket.

  • Gunakan impor absolut menggunakan seluruh nama paket ( CreateUser.pydalam Scripts/):

     from user_management.Modules import LDAPManager
    

Agar yang kedua berfungsi, paket user_managementharus diinstal di dalam PYTHONPATH. Selama pengembangan, Anda dapat mengonfigurasi IDE agar hal ini terjadi, tanpa harus menambahkan panggilan ke sys.path.appendmana pun secara manual .

Juga saya merasa aneh bahwa itu Scripts/adalah sub-paket. Karena dalam instalasi nyata user_managementmodul akan dipasang di bawah site-packagesberhasil ditemukan dalam lib/direktori (mana direktori yang digunakan untuk menginstal perpustakaan di OS Anda), sedangkan script harus dipasang di bawah bin/direktori (yang mana berisi executable untuk OS Anda).

Bahkan saya percaya Script/seharusnya tidak berada di bawah user_management. Itu harus pada level yang sama user_management. Dengan cara ini Anda tidak perlu menggunakan -m, tetapi Anda hanya perlu memastikan paket tersebut dapat ditemukan (lagi-lagi ini adalah masalah konfigurasi IDE, menginstal paket dengan benar atau menggunakan PYTHONPATH=. python Scripts/CreateUser.pyuntuk meluncurkan skrip dengan jalur yang benar).


Singkatnya, hierarki yang akan saya gunakan adalah:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |

 Scripts/  (*not* a package)
        |  
        |----- CreateUser.py
        |----- FindUser.py

Kemudian kode CreateUser.pydan FindUser.pyharus menggunakan impor absolut untuk mengimpor modul:

from user_management.Modules import LDAPManager

Selama instalasi Anda memastikan bahwa itu user_managementberakhir di suatu tempat di PYTHONPATH, dan skrip di dalam direktori untuk executable sehingga mereka dapat menemukan modul. Selama pengembangan, Anda bergantung pada konfigurasi IDE, atau Anda meluncurkan CreateUser.pypenambahan Scripts/direktori induk ke PYTHONPATH(maksud saya direktori yang berisi user_managementdan Scripts):

PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py

Atau Anda dapat memodifikasi PYTHONPATHsecara global sehingga Anda tidak perlu menentukan ini setiap saat. Pada OS unix (linux, Mac OS X, dll.) Anda dapat mengubah salah satu skrip shell untuk menentukan PYTHONPATHvariabel eksternal, pada Windows Anda harus mengubah pengaturan variabel lingkungan.


Tambahan saya percaya, jika Anda menggunakan python2, lebih baik memastikan untuk menghindari impor relatif implisit dengan meletakkan:

from __future__ import absolute_import

di bagian atas modul Anda. Dengan cara ini import X selalu berarti untuk mengimpor toplevel modul Xdan tidak akan pernah mencoba untuk mengimpor X.pyfile yang ada di direktori yang sama (jika direktori yang tidak di PYTHONPATH). Dengan cara ini satu - satunya cara untuk melakukan impor relatif adalah dengan menggunakan sintaks eksplisit (the from . import X), yang lebih baik ( eksplisit lebih baik daripada implisit ).

Ini akan memastikan Anda tidak pernah menggunakan impor relatif implisit yang "palsu", karena ini akan memunculkan ImportErrorsinyal yang jelas bahwa ada sesuatu yang salah. Jika tidak, Anda dapat menggunakan modul yang bukan seperti yang Anda pikirkan.

Bakuriu
sumber
Jika Anda menggunakan impor relatif, Anda harus menjalankanpython -m user_management.Scripts.CreateUser
mononoke
14

Dari Python 2.5 dan seterusnya, Anda dapat menggunakan

from ..Modules import LDAPManager

Periode memimpin membawa Anda "naik" satu tingkat dalam hierarki Anda.

Lihat dokumen Python tentang referensi intra-paket untuk impor.

jonrsharpe
sumber
3

Di "root" __init__.pyAnda juga bisa melakukan a

import sys
sys.path.insert(1, '.')

yang seharusnya membuat kedua modul dapat diimpor.

rdodev
sumber
0

Saya menghadapi masalah yang sama. Untuk mengatasi ini, saya menggunakan export PYTHONPATH="$PWD". Namun, dalam kasus ini, Anda perlu mengubah impor di Scriptsdirektori Anda bergantung pada hal di bawah ini:

Kasus 1: Jika Anda berada di user_managementdirektori, Anda scriptsharus menggunakan gaya ini from Modules import LDAPManageruntuk mengimpor modul.

Kasus 2: Jika Anda berada di luar user_managementlevel 1 main, Anda scriptsharus menggunakan gaya ini from user_management.Modules import LDAPManageruntuk mengimpor modul.

Peter Du
sumber