Impor relatif dalam Python 3

714

Saya ingin mengimpor fungsi dari file lain di direktori yang sama.

Kadang-kadang bekerja dengan saya from .mymodule import myfunctiontetapi kadang-kadang saya mendapatkan:

SystemError: Parent module '' not loaded, cannot perform relative import

Terkadang berhasil from mymodule import myfunction, tetapi terkadang saya juga mendapatkan:

SystemError: Parent module '' not loaded, cannot perform relative import

Saya tidak mengerti logikanya di sini, dan saya tidak dapat menemukan penjelasan apa pun. Ini terlihat sangat acak.

Bisakah seseorang menjelaskan kepada saya apa logika di balik semua ini?

John Smith Opsional
sumber
76
Ini berarti Anda menjalankan modul di dalam paket sebagai skrip. Hanya jalankan skrip dari luar paket.
Martijn Pieters
3
Mungkin Anda harus mendefinisikan kondisi Anda memiliki 'kadang-kadang' yang Anda sebutkan. Saya mengerti Anda tidak berarti Anda memiliki kesalahan acak.
joaquin
15
@ MartijnPieters: well, sayangnya, modul ini perlu berada di dalam paket, dan terkadang juga harus bisa dijalankan sebagai skrip. Adakah ide bagaimana saya bisa mencapainya?
John Smith Opsional
22
@JohnSmithOptional: Mencampur skrip di dalam paket itu rumit dan harus dihindari jika memungkinkan. Gunakan skrip pembungkus yang mengimpor paket dan menjalankan fungsi 'skrip' Anda sebagai gantinya.
Martijn Pieters
3
Sepertinya disayangkan. Saya membuat modul inti dengan kelas / metode yang dapat menguraikan / menganalisis jenis file tertentu, dan saya juga memiliki (terutama untuk saya sendiri) modul dan skrip terpisah yang mengimpornya - ini dapat memijat / mengonversi file-file tersebut. Tapi saya juga ingin dapat menyerahkan file inti tunggal (bukan paket kompleks keseluruhan) kepada pengguna akhir sehingga mereka dapat dengan mudah menempatkannya di sebelah file mereka dan menjalankannya. Dalam "mode skrip" itu, ia mem-parsing dan menganalisis file dan encoding, menghitung berbagai bidang / nilai / karakter khusus, dan memberikan laporan. Tapi itu sebenarnya tidak memodifikasi file. Anti-pola?
Jon Coombs

Jawaban:

528

sayangnya, modul ini perlu berada di dalam paket, dan terkadang juga harus bisa dijalankan sebagai skrip. Adakah ide bagaimana saya bisa mencapainya?

Cukup umum memiliki tata letak seperti ini ...

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py

... dengan mymodule.pyseperti ini ...

#!/usr/bin/env python3

# Exported function
def as_int(a):
    return int(a)

# Test function for module  
def _test():
    assert as_int('1') == 1

if __name__ == '__main__':
    _test()

... myothermodule.pyseperti ini ...

#!/usr/bin/env python3

from .mymodule import as_int

# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()

... dan main.pyseperti ini ...

#!/usr/bin/env python3

from mypackage.myothermodule import add

def main():
    print(add('1', '1'))

if __name__ == '__main__':
    main()

... yang berfungsi dengan baik saat Anda menjalankan main.pyatau mypackage/mymodule.py, tetapi gagal dengan mypackage/myothermodule.py, karena impor relatif ...

from .mymodule import as_int

Cara Anda menjalankannya adalah ...

python3 -m mypackage.myothermodule

... tapi agak verbose, dan tidak cocok dengan garis shebang seperti #!/usr/bin/env python3.

Perbaikan paling sederhana untuk kasus ini, dengan asumsi nama mymoduleitu unik secara global, akan menghindari menggunakan impor relatif, dan hanya menggunakan ...

from mymodule import as_int

... walaupun, jika tidak unik, atau struktur paket Anda lebih kompleks, Anda harus memasukkan direktori yang berisi direktori paket Anda PYTHONPATH, dan melakukannya seperti ini ...

from mypackage.mymodule import as_int

... atau jika Anda ingin itu berfungsi "di luar kotak", Anda dapat menemukan PYTHONPATHkode in terlebih dahulu dengan ini ...

import sys
import os

PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))

from mypackage.mymodule import as_int

Agak menyebalkan, tetapi ada petunjuk mengapa dalam email yang ditulis oleh Guido van Rossum tertentu ...

Saya -1 pada ini dan pada twiddling yang diusulkan lainnya dari __main__ mesin. Satu-satunya use case tampaknya menjalankan skrip yang kebetulan tinggal di dalam direktori modul, yang saya selalu lihat sebagai antipattern. Untuk membuat saya berubah pikiran, Anda harus meyakinkan saya bahwa itu tidak benar.

Apakah menjalankan skrip di dalam paket adalah antipattern atau tidak subjektif, tetapi secara pribadi saya merasa sangat berguna dalam paket yang saya miliki yang berisi beberapa widget wxPython kustom, jadi saya dapat menjalankan skrip untuk salah satu file sumber untuk menampilkan file yang wx.Frameberisi hanya berisi widget itu untuk tujuan pengujian.

Aya
sumber
7
Cara yang lebih baik untuk mendapatkan SCRIPTDIR diberikan dalam komentar Impor modul dari jalur relatif seolah- os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))olah Anda yakin bahwa modul Anda selalu layak fileAnda juga bisa menggunakan os.path.realpath(os.path.dirname(__file__)).
marcz
2
Anda dapat memperluas PYTHONPATH Anda dengan menerapkan potongan kode yang lebih pendek dan mudah dibaca: sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
Alex-Bogdanov
12
...which I've always seen as an antipattern.Saya tidak melihat bagaimana ini merupakan pola anti ... Sepertinya akan sangat nyaman jika impor relatif bekerja secara intuitif. Saya hanya ingin dapat mengimpor hal-hal yang saya tahu ada di direktori yang sama. Saya ingin tahu apa alasannya
YungGun
9
Guido menyerang lagi: nixing hal-hal yang bisa berguna. Yah itu tidak akan terjadi lagi.
javadba
4
Ini adalah hal yang paling menyedihkan yang pernah saya lihat tentang Python.
AtilioA
263

Penjelasan

Dari PEP 328

Impor relatif menggunakan atribut __name__ modul untuk menentukan posisi modul dalam hierarki paket. Jika nama modul tidak mengandung informasi paket apa pun (mis. Diatur ke '__main__') maka impor relatif diselesaikan seolah-olah modul tersebut adalah modul tingkat atas , terlepas dari di mana letak sebenarnya modul pada sistem file.

Pada titik tertentu, PEP 338 bertentangan dengan PEP 328 :

... impor relatif mengandalkan __name__ untuk menentukan posisi modul saat ini dalam hirarki paket. Dalam modul utama, nilai __name__ selalu '__main__' , jadi impor relatif eksplisit akan selalu gagal (karena mereka hanya bekerja untuk modul di dalam paket)

dan untuk mengatasi masalah ini, PEP 366 memperkenalkan variabel tingkat atas __package__:

Dengan menambahkan atribut level modul baru, PEP ini memungkinkan impor relatif untuk bekerja secara otomatis jika modul dijalankan menggunakan sakelar -m . Sejumlah kecil boilerplate dalam modul itu sendiri akan memungkinkan impor relatif bekerja ketika file dijalankan berdasarkan nama. [...] Ketika [atribut] hadir, impor relatif akan didasarkan pada atribut ini daripada atribut modul __name__ . [...] Ketika modul utama ditentukan oleh nama filenya, maka atribut __package__ akan diatur ke None . [...] __name __. rpartition ('.') [0] untuk modul normal dan __name__ untuk modul inisialisasi paket) Ketika sistem impor bertemu impor relatif eksplisit dalam modul tanpa __package__ set (atau dengan itu diatur ke None), itu akan menghitung dan menyimpan nilai yang benar (

(penekanan milikku)

Jika __name__adalah '__main__', __name__.rpartition('.')[0]mengembalikan string kosong. Inilah sebabnya mengapa ada string kosong literal dalam deskripsi kesalahan:

SystemError: Parent module '' not loaded, cannot perform relative import

Bagian yang relevan dari CPython ini PyImport_ImportModuleLevelObjectfungsi :

if (PyDict_GetItem(interp->modules, package) == NULL) {
    PyErr_Format(PyExc_SystemError,
            "Parent module %R not loaded, cannot perform relative "
            "import", package);
    goto error;
}

CPython memunculkan pengecualian ini jika tidak dapat menemukan package(nama paket) di interp->modules(dapat diakses sebagai sys.modules). Karena sys.modulesmerupakan "kamus yang memetakan nama-nama modul untuk modul yang telah dimuat" , sekarang jelas bahwa modul orang tua harus secara eksplisit mutlak-impor sebelum melakukan impor relatif .

Catatan: Patch dari masalah 18018 telah menambahkan blok lainif , yang akan dieksekusi sebelum kode di atas:

if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
    PyErr_SetString(PyExc_ImportError,
            "attempted relative import with no known parent package");
    goto error;
} /* else if (PyDict_GetItem(interp->modules, package) == NULL) {
    ...
*/

Jika package(sama seperti di atas) adalah string kosong, pesan kesalahannya adalah

ImportError: attempted relative import with no known parent package

Namun, Anda hanya akan melihat ini dalam Python 3.6 atau lebih baru.

Solusi # 1: Jalankan skrip Anda menggunakan -m

Pertimbangkan direktori (yang merupakan paket Python ):

.
├── package
│   ├── __init__.py
│   ├── module.py
│   └── standalone.py

Semua file dalam paket dimulai dengan 2 baris kode yang sama:

from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())

Saya menyertakan dua baris ini hanya untuk membuat urutan operasi jelas. Kami dapat mengabaikan mereka sepenuhnya, karena mereka tidak mempengaruhi eksekusi.

__init__.py dan module.py hanya berisi dua baris tersebut (yaitu, keduanya kosong secara efektif).

standalone.py juga mencoba mengimpor module.py melalui impor relatif:

from . import module  # explicit relative import

Kami sadar bahwa itu /path/to/python/interpreter package/standalone.pyakan gagal. Namun, kita dapat menjalankan modul dengan -mopsi baris perintah yang akan "mencari sys.pathmodul yang bernama dan mengeksekusi isinya sebagai __main__modul" :

vaultah@base:~$ python3 -i -m package.standalone
Importing /home/vaultah/package/__init__.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/module.py
>>> __file__
'/home/vaultah/package/standalone.py'
>>> __package__
'package'
>>> # The __package__ has been correctly set and module.py has been imported.
... # What's inside sys.modules?
... import sys
>>> sys.modules['__main__']
<module 'package.standalone' from '/home/vaultah/package/standalone.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>

-mmelakukan semua barang impor untuk Anda dan secara otomatis ditetapkan __package__, tetapi Anda dapat melakukannya sendiri di menu

Solusi # 2: Set __package__ secara manual

Harap perlakukan itu sebagai bukti konsep daripada solusi yang sebenarnya. Itu tidak cocok untuk digunakan dalam kode dunia nyata.

PEP 366 memiliki solusi untuk masalah ini, namun, itu tidak lengkap, karena pengaturan __package__saja tidak cukup. Anda akan perlu mengimpor setidaknya N paket sebelumnya dalam hierarki modul, tempat N adalah jumlah direktori induk (relatif terhadap direktori skrip) yang akan dicari untuk modul yang sedang diimpor.

Jadi,

  1. Tambahkan direktori induk dari Nth pendahulu modul saat inisys.path

  2. Hapus direktori file saat ini dari sys.path

  3. Impor modul induk modul saat ini menggunakan nama yang sepenuhnya memenuhi syarat

  4. Setel __package__ke nama yang sepenuhnya memenuhi syarat dari 2

  5. Lakukan impor relatif

Saya akan meminjam file dari Solusi # 1 dan menambahkan beberapa sub paket lagi:

package
├── __init__.py
├── module.py
└── subpackage
    ├── __init__.py
    └── subsubpackage
        ├── __init__.py
        └── standalone.py

Kali ini standalone.py akan mengimpor module.py dari paket paket menggunakan impor relatif berikut

from ... import module  # N = 3

Kita harus mendahului baris itu dengan kode boilerplate, untuk membuatnya bekerja.

import sys
from pathlib import Path

if __name__ == '__main__' and __package__ is None:
    file = Path(__file__).resolve()
    parent, top = file.parent, file.parents[3]

    sys.path.append(str(top))
    try:
        sys.path.remove(str(parent))
    except ValueError: # Already removed
        pass

    import package.subpackage.subsubpackage
    __package__ = 'package.subpackage.subsubpackage'

from ... import module # N = 3

Hal ini memungkinkan kita untuk mengeksekusi standalone.py dengan nama file:

vaultah@base:~$ python3 package/subpackage/subsubpackage/standalone.py
Running /home/vaultah/package/subpackage/subsubpackage/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/subpackage/__init__.py
Importing /home/vaultah/package/subpackage/subsubpackage/__init__.py
Importing /home/vaultah/package/module.py

Solusi yang lebih umum yang dibungkus dalam suatu fungsi dapat ditemukan di sini . Contoh penggunaan:

if __name__ == '__main__' and __package__ is None:
    import_parents(level=3) # N = 3

from ... import module
from ...module.submodule import thing

Solusi # 3: Gunakan impor absolut dan alat setup

Langkah-langkahnya adalah -

  1. Ganti impor relatif eksplisit dengan impor absolut setara

  2. Instal packageuntuk membuatnya dapat diimpor

Sebagai contoh, struktur direktori mungkin sebagai berikut

.
├── project
│   ├── package
│   │   ├── __init__.py
│   │   ├── module.py
│   │   └── standalone.py
│   └── setup.py

di mana setup.py berada

from setuptools import setup, find_packages
setup(
    name = 'your_package_name',
    packages = find_packages(),
)

Sisa file dipinjam dari Solusi # 1 .

Instalasi akan memungkinkan Anda untuk mengimpor paket terlepas dari direktori kerja Anda (dengan asumsi tidak akan ada masalah penamaan).

Kami dapat memodifikasi standalone.py untuk menggunakan keunggulan ini (langkah 1):

from package import module  # absolute import

Ubah direktori kerja Anda projectdan jalankan /path/to/python/interpreter setup.py install --user( --userinstal paket di direktori paket situs Anda ) (langkah 2):

vaultah@base:~$ cd project
vaultah@base:~/project$ python3 setup.py install --user

Mari kita verifikasi bahwa sekarang mungkin untuk menjalankan standalone.py sebagai skrip:

vaultah@base:~/project$ python3 -i package/standalone.py
Running /home/vaultah/project/package/standalone.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>

Catatan : Jika Anda memutuskan untuk turun rute ini, Anda akan lebih baik menggunakan lingkungan virtual untuk menginstal paket secara terpisah.

Solusi # 4: Gunakan impor absolut dan beberapa kode boilerplate

Terus terang, instalasi tidak diperlukan - Anda dapat menambahkan beberapa kode boilerplate ke skrip Anda untuk membuat impor absolut berfungsi.

Saya akan meminjam file dari Solution # 1 dan mengubah standalone.py :

  1. Menambahkan direktori induk dari paket untuk sys.path sebelum mencoba apa pun impor dari paket menggunakan impor absolut:

    import sys
    from pathlib import Path # if you haven't already done so
    file = Path(__file__).resolve()
    parent, root = file.parent, file.parents[1]
    sys.path.append(str(root))
    
    # Additionally remove the current file's directory from sys.path
    try:
        sys.path.remove(str(parent))
    except ValueError: # Already removed
        pass
  2. Ganti impor relatif dengan impor absolut:

    from package import module  # absolute import

standalone.py berjalan tanpa masalah:

vaultah@base:~$ python3 -i package/standalone.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>

Saya merasa bahwa saya harus memperingatkan Anda: cobalah untuk tidak melakukan ini, terutama jika proyek Anda memiliki struktur yang kompleks.


Sebagai catatan tambahan, PEP 8 merekomendasikan penggunaan impor absolut, tetapi menyatakan bahwa dalam beberapa skenario, impor relatif eksplisit dapat diterima:

Impor absolut direkomendasikan, karena biasanya lebih mudah dibaca dan cenderung berperilaku lebih baik (atau setidaknya memberikan pesan kesalahan yang lebih baik). [...] Namun, impor relatif eksplisit adalah alternatif yang dapat diterima untuk impor absolut, terutama ketika berhadapan dengan tata letak paket yang rumit di mana menggunakan impor absolut akan menjadi kata yang tidak perlu.

kubah
sumber
3
Apakah mungkin untuk mengatur __package__secara manual jika nama __main__untuk menyelesaikan masalah?
Paulo Scardine
Terima kasih, jawaban yang bagus! Saya dapat memuat modul menggunakan impmodul dan mengatur __package__sesuai tetapi hasilnya jelas anti-pola.
Paulo Scardine
Saya mendapatkan kesalahan AttributeError: 'PosixPath' object has no attribute 'path'.
pengguna
Terima kasih atas balasan cepat. Saya menggunakan paket nltk, saya mendapatkan pesan kesalahan: `File" /usr/local/lib/python3.5/dist-packages/nltk/__init__.py ", baris 115, dalam <module> dari nltk.decorators import decorator, memoize File "/usr/local/lib/python3.5/dist-packages/nltk/decorators.py", baris 23, di <module> sys.path = [p untuk p di sys.path if "nltk "not in p] File" /usr/local/lib/python3.5/dist-packages/nltk/decorators.py ", baris 23, di <listcomp> sys.path = [p untuk p di sys.path if" nltk "not in p] TypeError: argumen tipe 'PosixPath' tidak dapat diubah`
pengguna
1
Anda juga dapat mengimpor file dengan path file (relatif juga): docs.python.org/3/library/…
Ctrl-C
86

Masukkan ini ke dalam file __init__.py paket Anda :

# For relative imports to work in Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))

Dengan asumsi paket Anda seperti ini:

├── project
   ├── package
      ├── __init__.py
      ├── module1.py
      └── module2.py
   └── setup.py

Sekarang gunakan impor reguler dalam paket Anda, seperti:

# in module2.py
from module1 import class1

Ini berfungsi di kedua python 2 dan 3.

am5
sumber
1
apakah ini berfungsi jika kita mengemasnya sebagai weel
Alex Punnen
1
Saya juga berpikir ini pantas mendapat lebih banyak suara. Menempatkan ini di setiap __init__.pydasarnya akan menyelesaikan semua kesalahan impor relatif.
frankliuao
3
Saya tidak dapat berbicara untuk orang lain, tetapi saya cenderung menghindari modifikasi sys.pathkarena saya khawatir ini akan memengaruhi kode lain. (Sebagian ini karena saya tidak tahu seluk-beluk cara kerjanya.)
pianoJames
@pianoJames Saya tahu apa yang Anda maksud, perbaikan sulap ini (tampaknya, setelah banyak bermain-main) sepertinya sedikit terlalu mudah. Tapi itu berhasil. Akan tertarik tidak tahu dari mereka-itu-tahu apakah ini memiliki efek samping negatif.
Jon
Saya menggunakan ini sekarang: dan sejauh ini sangat baik.
javadba
37

Saya mengalami masalah ini. Solusi hack sedang mengimpor melalui blok if / else seperti berikut:

#!/usr/bin/env python3
#myothermodule

if __name__ == '__main__':
    from mymodule import as_int
else:
    from .mymodule import as_int


# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()
goffer
sumber
29
itu bukan solusi yang sangat bagus. juga, telanjang except:itu buruk. gunakan except ImportError:saja!
ThiefMaster
6
Itu SystemErrordisini. (Py 3.4)
Avi
8
Ini bukan ide yang buruk, tetapi akan lebih baik untuk mendeteksi impor mana yang harus digunakan daripada mencoba / kecuali. Sesuatu seperti if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int.
Perkins
@ Perkins Yah ... dalam banyak kasus tidak . Saya pikir impor relatif mungkin merupakan pengecualian.
wizzwizz4
8

Mudah-mudahan, ini akan bernilai bagi seseorang di luar sana - saya telah melalui setengah lusin tumpukan stackoverflow mencoba untuk mencari tahu impor relatif mirip dengan apa yang diposting di atas sini. Saya mengatur semuanya seperti yang disarankan tetapi saya masih memukulModuleNotFoundError: No module named 'my_module_name'

Karena saya baru saja mengembangkan secara lokal dan bermain-main, saya belum membuat / menjalankan setup.pyfile. Saya juga belum mengaturnya PYTHONPATH.

Saya menyadari bahwa ketika saya menjalankan kode saya ketika tes berada di direktori yang sama dengan modul, saya tidak dapat menemukan modul saya:

$ python3 test/my_module/module_test.py                                                                                                               2.4.0
Traceback (most recent call last):
  File "test/my_module/module_test.py", line 6, in <module>
    from my_module.module import *
ModuleNotFoundError: No module named 'my_module'

Namun, ketika saya secara spesifik menentukan jalur, segalanya mulai berfungsi:

$ PYTHONPATH=. python3 test/my_module/module_test.py                                                                                                  2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s

OK

Jadi, jika ada yang mencoba beberapa saran, yakin kode mereka terstruktur dengan benar dan masih menemukan diri mereka dalam situasi yang sama seperti saya mencoba salah satu dari berikut ini jika Anda tidak mengekspor direktori saat ini ke PYTHONPATH Anda:

  1. Jalankan kode Anda dan secara eksplisit sertakan path seperti: $ PYTHONPATH=. python3 test/my_module/module_test.py
  2. Untuk menghindari panggilan PYTHONPATH=., buat setup.pyfile dengan konten seperti berikut dan jalankan python setup.py developmentuntuk menambahkan paket ke path:
# setup.py
from setuptools import setup, find_packages

setup(
    name='sample',
    packages=find_packages()
)
LaCroixed
sumber
6

Saya perlu menjalankan python3 dari direktori proyek utama untuk membuatnya berfungsi.

Misalnya, jika proyek memiliki struktur berikut:

project_demo/
├── main.py
├── some_package/
   ├── __init__.py
   └── project_configs.py
└── test/
    └── test_project_configs.py

Larutan

Saya akan menjalankan python3 di dalam folder project_demo / dan kemudian melakukan a

from some_package import project_configs
Thrthur
sumber
4

Untuk mengatasi masalah ini, saya menemukan solusi dengan paket repackage , yang telah bekerja untuk saya selama beberapa waktu. Itu menambahkan direktori atas ke lib path:

import repackage
repackage.up()
from mypackage.mymodule import myfunction

Pengemasan ulang dapat membuat impor relatif yang bekerja dalam berbagai kasus, menggunakan strategi cerdas (memeriksa tumpukan panggilan).

fralau
sumber
Sejauh ini solusi termudah! Terima kasih!
CodingInCircles
1
Terima kasih! Alih-alih mencoba memberikan jawaban terbaik, saya mencoba memberikan jawaban yang dapat digunakan :-)
fralau
3

jika kedua paket berada di jalur impor Anda (sys.path), dan modul / kelas yang Anda inginkan adalah contoh / example.py, maka untuk mengakses kelas tanpa impor relatif coba:

from example.example import fkt
Salt999
sumber
1

Saya pikir solusi terbaik adalah membuat paket untuk modul Anda: Di sini adalah info lebih lanjut tentang cara melakukannya.

Setelah Anda memiliki paket Anda tidak perlu khawatir tentang impor relatif, Anda bisa melakukan impor mutlak.

Taras Kucherenko
sumber
0

Saya memiliki masalah serupa: Saya membutuhkan layanan Linux dan plugin cgi yang menggunakan konstanta umum untuk bekerja sama. Cara 'alami' untuk melakukan ini adalah menempatkan mereka di init .py dari paket, tetapi saya tidak dapat memulai plugin cgi dengan parameter -m.

Solusi terakhir saya mirip dengan Solusi # 2 di atas:

import sys
import pathlib as p
import importlib

pp = p.Path(sys.argv[0])
pack = pp.resolve().parent

pkg = importlib.import_module('__init__', package=str(pack))

Kerugiannya adalah Anda harus mengawali konstanta (atau fungsi umum) dengan pkg:

print(pkg.Glob)
Thomas Heckmann
sumber