Bagaimana cara mengimpor kelas Python yang ada di direktori di atas?

212

Saya ingin mewarisi dari kelas dalam file yang terletak di direktori di atas yang sekarang.

Apakah mungkin untuk mengimpor file itu secara relatif?

pengguna129975
sumber

Jawaban:

177

from ..subpkg2 import mod

Per dokumen Python: Saat berada di dalam hierarki paket, gunakan dua titik, seperti yang dikatakan oleh dokumen pernyataan impor :

Saat menentukan modul apa yang akan diimpor, Anda tidak perlu menentukan nama absolut modul. Ketika modul atau paket terkandung dalam paket lain, dimungkinkan untuk melakukan impor relatif dalam paket teratas yang sama tanpa harus menyebutkan nama paket. Dengan menggunakan titik utama dalam modul atau paket yang ditentukan setelah fromAnda dapat menentukan seberapa tinggi untuk melintasi hierarki paket saat ini tanpa menentukan nama yang tepat. Satu titik terkemuka berarti paket saat ini di mana modul membuat impor ada. Dua titik berarti naik satu tingkat paket . Tiga titik naik dua tingkat, dll. Jadi jika Anda mengeksekusi from . import moddari modul dalam pkgpaket maka Anda akan berakhir mengimpor pkg.mod. Jika Anda mengeksekusi . Spesifikasi untuk impor relatif terdapat di dalamnyafrom ..subpkg2 import mod dari dalam, pkg.subpkg1Anda akan mengimporpkg.subpkg2.modPEP 328 .

PEP 328 berkaitan dengan impor absolut / relatif.

Gimel
sumber
4
up1 = os.path.abspath ('..') sys.path.insert (0, up1)
rp.
Pep 328 hanya menampilkan Versi Python: 2.4, 2.5, 2.6. Versi 3 diserahkan kepada jiwa yang lebih berpengetahuan.
gimel
22
Ini memicu kesalahan ValueError: upaya impor relatif di luar paket tingkat atas
Carlo
4
Hasil dalam ImportError: percobaan impor relatif tanpa paket induk yang dikenal
Rotkiv
116
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Sepero
sumber
1
ini bekerja untuk saya. Setelah menambahkan ini, saya dapat langsung mengimpor modul induk, tidak perlu menggunakan "..".
Evan Hu
8
itu hanya bekerja, secara kasar, jika nilai PWD aplikasi - directeroy saat ini, adalah anak dari orangtua. Jadi, bahkan jika itu berhasil, banyak jenis perubahan sistemik dapat mengusirnya.
Phlip
Ini bekerja untuk saya juga untuk mengimpor modul tingkat yang lebih tinggi. Saya menggunakan ini dengan os.chdir ("..") untuk memuat file lain dari level yang lebih tinggi.
Surendra Shrestha
Ini sepertinya memicu kesalahan yang sama dengan jawaban di atas dari gimel.
Carlo
81

Jawaban @ gimel benar jika Anda dapat menjamin hierarki paket yang ia sebutkan. Jika Anda tidak bisa - jika kebutuhan nyata Anda adalah seperti yang Anda ungkapkan, secara eksklusif terkait dengan direktori dan tanpa hubungan yang perlu dengan pengemasan - maka Anda perlu bekerja __file__untuk mengetahui direktori induk (beberapa os.path.dirnamepanggilan akan dilakukan; -), maka (jika direktori yang tidak sudah di sys.path) tambahkan sementara insert mengatakan dir di bagian paling mulai dari sys.path, __import__, menghapus kata dir lagi - pekerjaan berantakan memang, tapi, "ketika Anda harus, Anda harus" (dan kemudian berusaha Pyhon untuk jangan pernah menghentikan programmer dari melakukan apa yang harus dilakukan - seperti standar ISO C mengatakan di bagian "Spirit of C" di kata pengantar! -).

Berikut ini contoh yang mungkin cocok untuk Anda:

import sys
import os.path
sys.path.append(
    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))

import module_in_parent_dir
Alex Martelli
sumber
2
ini mungkin menambahkan direktori yang ada di dalam paket Python sys.pathsehingga membuat modul yang sama tersedia dengan nama yang berbeda dan semua bug yang sesuai. autopath.py di pypy atau _preamble.py di twisted menyelesaikannya dengan menggunakan kriteria pencarian yang mengidentifikasi paket tingkat atas sambil melintasi direktori ke atas.
jfs
4
Anda mungkin ingin melakukan sesuatu seperti sys.path.remove(pathYouJustAdded)setelah impor yang Anda butuhkan agar tidak mempertahankan jalur baru ini.
Matthias
35

Impor modul dari direktori yang persis satu tingkat di atas direktori saat ini:

from .. import module
Sepero
sumber
38
Saya mendapat: mencoba impor relatif di luar paket tingkat atas :(
RicardoE
25
menggunakan from .. import module saya mendapat error ValueError: Mencoba impor relatif dalam non-paket dengan mengikuti rekomendasi
user3428154
4

Cara memuat modul yang merupakan direktori

kata pengantar: Saya melakukan penulisan ulang substansial dari jawaban sebelumnya dengan harapan membantu memudahkan orang ke dalam ekosistem python, dan mudah-mudahan memberi setiap orang perubahan sukses terbaik dengan sistem impor python.

Ini akan mencakup impor relatif dalam satu paket , yang menurut saya merupakan kasus yang paling memungkinkan untuk pertanyaan OP.

Python adalah sistem modular

Inilah sebabnya kami menulis import foountuk memuat modul "foo" dari root namespace, alih-alih menulis:

foo = dict();  # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh:  # please avoid doing this
    exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo)  # please avoid doing this

Python tidak digabungkan ke sistem file

Inilah sebabnya mengapa kita dapat menanamkan python di lingkungan di mana tidak ada sistem file defacto tanpa menyediakan yang virtual, seperti Jython.

Karena dipisahkan dari sistem file memungkinkan impor menjadi fleksibel, desain ini memungkinkan untuk hal-hal seperti impor dari arsip / file zip, impor singleton, caching bytecode, ekstensi cffi, bahkan pemuatan definisi kode jarak jauh.

Jadi jika impor tidak digabungkan ke sistem file, apa artinya "satu direktori"? Kita harus memilih beberapa heuristik tetapi kita bisa melakukan itu, misalnya ketika bekerja di dalam suatu paket , beberapa heuristik telah didefinisikan yang membuat impor relatif suka .foodan..foo bekerja dalam paket yang sama. Keren!

Jika Anda sungguh-sungguh ingin memasangkan pola pemuatan kode sumber Anda ke sistem file, Anda dapat melakukannya. Anda harus memilih heuristik Anda sendiri, dan menggunakan beberapa jenis mesin impor, saya sarankan importlib

Contoh importlib Python terlihat seperti ini:

import importlib.util
import sys

# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'

foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)

foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton

Pengemasan

Ada contoh proyek bagus yang tersedia secara resmi di sini: https://github.com/pypa/sampleproject

Paket python adalah kumpulan informasi tentang kode sumber Anda, yang dapat memberi tahu alat lain cara menyalin kode sumber Anda ke komputer lain, dan cara mengintegrasikan kode sumber Anda ke jalur sistem itu sehingga import foobekerja untuk komputer lain (terlepas dari penerjemah, sistem operasi host, dll)

Struktur Direktori

Mari kita memiliki nama paket foo, di beberapa direktori (lebih disukai direktori kosong).

some_directory/
    foo.py  # `if __name__ == "__main__":`  lives here

Preferensi saya adalah membuat setup.pysebagai saudara foo.py, karena membuat menulis file setup.py lebih sederhana, namun Anda dapat menulis konfigurasi untuk mengubah / mengarahkan ulang semua yang dilakukan setuptools secara default jika Anda mau; misalnya meletakkan di foo.pybawah direktori "src /" agak populer, tidak dibahas di sini.

some_directory/
    foo.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    py_modules=['foo'],
)

.

python3 -m pip install --editable ./  # or path/to/some_directory/

"dapat diedit" alias -eakan mengarahkan kembali mesin pengimpor untuk memuat file sumber dalam direktori ini, alih-alih menyalin file yang sekarang aktif ke pustaka lingkungan pemasangan. Ini juga dapat menyebabkan perbedaan perilaku pada mesin pengembang, pastikan untuk menguji kode Anda! Ada alat selain pip, namun saya akan merekomendasikan pip menjadi pengantar :)

Saya juga suka membuat foo"paket" (direktori berisi __init__.py) alih-alih modul (file ".py" tunggal), baik "paket" dan "modul" dapat dimuat ke root namespace, modul memungkinkan ruang nama bersarang, yang sangat membantu jika kita ingin mengimpor "relatif satu direktori".

some_directory/
    foo/
        __init__.py
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
)

Saya juga suka membuat foo/__main__.py, ini memungkinkan python untuk mengeksekusi paket sebagai modul, mis. python3 -m fooAkan dieksekusi foo/__main__.pysebagai __main__.

some_directory/
    foo/
        __init__.py
        __main__.py  # `if __name__ == "__main__":`  lives here, `def main():` too!
    setup.py

.

#!/usr/bin/env python3
# setup.py

import setuptools

setuptools.setup(
    name="foo",
    ...
    packages=['foo'],
    ...
    entry_points={
        'console_scripts': [
            # "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
            'foo=foo.__main__:main',
        ]
    },
)

Mari kita selesaikan ini dengan beberapa modul lagi: Pada dasarnya, Anda dapat memiliki struktur direktori seperti ini:

some_directory/
    bar.py           # `import bar`
    foo/
        __init__.py  # `import foo`
        __main__.py
        baz.py       # `import foo.baz
        spam/           
            __init__.py  # `import foo.spam`
            eggs.py      # `import foo.spam.eggs`
    setup.py

setup.py secara konvensional menyimpan informasi metadata tentang kode sumber di dalamnya, seperti:

  • dependensi apa yang diperlukan untuk menginstal bernama "install_requires"
  • nama apa yang harus digunakan untuk manajemen paket (install / uninstall "nama"), saya sarankan ini cocok dengan nama paket python utama Anda dalam kasus kami foo , meskipun mengganti garis bawah untuk tanda hubung sangat populer
  • informasi lisensi
  • tag jatuh tempo (alpha / beta / dll),
  • tag audiens (untuk pengembang, untuk pembelajaran mesin, dll),
  • konten dokumentasi satu halaman (seperti README),
  • nama shell (nama yang Anda ketik di shell pengguna seperti bash, atau nama yang Anda temukan di shell pengguna grafis seperti menu mulai),
  • daftar modul python paket ini akan menginstal (dan menghapus)
  • titik masuk "jalankan tes" defacto python ./setup.py test

Sangat ekspansif, bahkan dapat mengkompilasi ekstensi c on the fly jika modul sumber sedang diinstal pada mesin pengembangan. Untuk contoh setiap hari, saya sarankan setup.py PYPA Sample Repository

Jika Anda merilis artefak build, misalnya salinan kode yang dimaksudkan untuk menjalankan komputer yang hampir identik, file requirement.txt adalah cara populer untuk mengambil snapshot informasi dependensi yang tepat, di mana "install_requires" adalah cara yang baik untuk menangkap minimum dan versi kompatibel maksimum. Namun, mengingat bahwa mesin target hampir identik, saya sangat merekomendasikan membuat tarball dari seluruh awalan python. Ini bisa rumit, terlalu rinci untuk masuk ke sini. Periksa pip install's --targetpilihan, atau virtualenv alias venv untuk memimpin.

kembali ke contoh

cara mengimpor file satu direktori ke atas:

Dari foo / spam / eggs.py, jika kita menginginkan kode dari foo / baz kita bisa memintanya dengan namespace absolutnya:

import foo.baz

Jika kami ingin menyimpan kapabilitas untuk memindahkan eggs.py ke beberapa direktori lain di masa mendatang dengan beberapa bazimplementasi relatif lainnya , kami dapat menggunakan impor relatif seperti:

import ..baz
ThorSummoner
sumber
-5

Python adalah sistem modular

Python tidak bergantung pada sistem file

Untuk memuat kode python dengan andal, masukkan kode itu ke dalam modul, dan modul itu dipasang di pustaka python.

Modul yang diinstal selalu dapat dimuat dari namespace tingkat atas dengan import <name>


Ada proyek contoh hebat yang tersedia secara resmi di sini: https://github.com/pypa/sampleproject

Pada dasarnya, Anda dapat memiliki struktur direktori seperti ini:

the_foo_project/
    setup.py  

    bar.py           # `import bar`
    foo/
      __init__.py    # `import foo`

      baz.py         # `import foo.baz`

      faz/           # `import foo.faz`
        __init__.py
        daz.py       # `import foo.faz.daz` ... etc.

.

Pastikan untuk menyatakan setuptools.setup()di Anda setup.py,

contoh resmi: https://github.com/pypa/sampleproject/blob/master/setup.py

Dalam kasus kami, kami mungkin ingin mengekspor bar.pydan foo/__init__.py, contoh singkat saya:

setup.py

#!/usr/bin/env python3

import setuptools

setuptools.setup(
    ...
    py_modules=['bar'],
    packages=['foo'],
    ...
    entry_points={}, 
        # Note, any changes to your setup.py, like adding to `packages`, or
        # changing `entry_points` will require the module to be reinstalled;
        # `python3 -m pip install --upgrade --editable ./the_foo_project
)

.

Sekarang kita dapat menginstal modul kita ke pustaka python; dengan pip, Anda dapat menginstal the_foo_projectke pustaka python Anda dalam mode edit, sehingga kami dapat bekerja secara real time

python3 -m pip install --editable=./the_foo_project

# if you get a permission error, you can always use 
# `pip ... --user` to install in your user python library

.

Sekarang dari konteks python apa pun, kita dapat memuat py_modules dan paket bersama kita

foo_script.py

#!/usr/bin/env python3

import bar
import foo

print(dir(bar))
print(dir(foo))
ThorSummoner
sumber
2
harus menyebutkan saya selalu menginstal modul saya sementara saya mengerjakannya dengan pip install --edit foo, hampir selalu di dalam virtualenv. Saya hampir tidak pernah menulis modul yang tidak dimaksudkan untuk diinstal. jika saya salah mengerti sesuatu yang ingin saya ketahui.
ThorSummoner
Saya juga harus menyebutkan bahwa menggunakan suite uji venv-auto-creation, seperti tox sangat membantu karena editabletautan telur dan modul python yang dipasang tidak persis sama dalam segala hal; misalnya, menambahkan namespace baru ke modul yang dapat diedit akan ditemukan di pencarian path Anda, tetapi jika itu tidak diekspor dalam file setup.py Anda tidak akan dikemas / diinstal! Uji kasus penggunaan Anda :)
ThorSummoner