Impor relatif untuk miliar kali

717

Saya pernah ke sini:

dan banyak URL yang tidak saya salin, beberapa di SO, beberapa di situs lain, ketika saya pikir saya akan mendapatkan solusinya dengan cepat.

Pertanyaan yang selalu berulang adalah ini: Dengan Windows 7, 32-bit Python 2.7.3, bagaimana cara saya menyelesaikan pesan "Upaya impor relatif bukan paket" ini? Saya membangun replika yang tepat dari paket di pep-0328:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

Impor dilakukan dari konsol.

Saya memang membuat fungsi bernama spam dan telur dalam modul yang sesuai. Tentu, itu tidak berhasil. Jawabannya rupanya ada di URL ke-4 yang saya daftarkan, tetapi semuanya untuk saya. Ada tanggapan ini di salah satu URL yang saya kunjungi:

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

Tanggapan di atas terlihat menjanjikan, tetapi semua hieroglif bagi saya. Jadi pertanyaan saya, bagaimana cara membuat Python tidak kembali kepada saya "Mencoba impor relatif dalam paket non"? memiliki jawaban yang melibatkan -m, seharusnya.

Dapatkah seseorang tolong beri tahu saya mengapa Python memberikan pesan kesalahan itu, apa artinya dengan "non-paket", mengapa dan bagaimana Anda mendefinisikan 'paket', dan jawaban yang tepat cukup mudah dipahami oleh anak TK untuk mengerti .

Paul Roub
sumber
5
Bagaimana Anda mencoba menggunakan file yang Anda perlihatkan? Apa kode yang Anda jalankan?
BrenBarn
Lihat python.org/dev/peps/pep-0328 . Saya menggunakan format paket yang saya jelaskan di posting saya. The init file py kosong. moduleY.py memiliki def spam(): pass, moduleA.py memiliki def eggs(): pass. Saya mencoba mengeksekusi beberapa perintah "dari. Sesuatu impor", tetapi mereka tidak berhasil. Sekali lagi, lihat pep-0328.
6
Lihat jawaban saya. Anda masih belum sepenuhnya mengklarifikasi apa yang Anda lakukan, tetapi jika Anda mencoba melakukannya from .something import somethingdalam interpreter interaktif, itu tidak akan berhasil. Impor relatif hanya dapat digunakan dalam modul, bukan secara interaktif.
BrenBarn
105
Fakta bahwa "miliaran" orang - ok 83.136 pada komentar ini - mengalami cukup kesulitan dengan impor untuk mencari pertanyaan ini; kita hanya dapat menyimpulkan bahwa impor python adalah kontra-intuitif bagi banyak orang, jika tidak sebagian besar programmer. Guido, mungkin Anda harus menerima ini dan meminta komite untuk mendesain ulang mekanisme impor. Minimal, sintaks ini seharusnya berfungsi jika x.py dan z.py berada di direktori yang sama. Yaitu jika x.py memiliki pernyataan, "dari .z import MyZebraClass" x harus mengimpor z BAHKAN jika dijalankan sebagai utama ! Mengapa begitu sulit?
Steve L
4
Setelah membaca sebagian besar utas ini, meskipun bukan jawaban untuk pertanyaan, "cukup gunakan impor absolut" tampaknya menjadi solusi ...
CodeJockey

Jawaban:

1043

Script vs. Modul

Berikut penjelasannya. Versi singkatnya adalah bahwa ada perbedaan besar antara langsung menjalankan file Python, dan mengimpor file itu dari tempat lain. Hanya mengetahui di mana direktori file tidak menentukan paket apa yang menurut Python masuk. Selain itu, tergantung pada bagaimana Anda memuat file ke Python (dengan menjalankan atau dengan mengimpor).

Ada dua cara untuk memuat file Python: sebagai skrip tingkat atas, atau sebagai modul. File dimuat sebagai skrip tingkat atas jika Anda menjalankannya secara langsung, misalnya dengan mengetik python myfile.pypada baris perintah. Itu dimuat sebagai modul jika Anda melakukannya python -m myfile, atau jika dimuat ketika importpernyataan dijumpai di dalam beberapa file lain. Hanya ada satu skrip tingkat atas pada satu waktu; skrip tingkat atas adalah file Python yang Anda jalankan untuk memulai sesuatu.

Penamaan

Ketika sebuah file dimuat, ia diberi nama (yang disimpan dalam __name__atributnya). Jika dimuat sebagai skrip tingkat atas, namanya adalah __main__. Jika itu dimuat sebagai modul, namanya adalah nama file, didahului dengan nama paket / subpackages yang merupakan bagiannya, dipisahkan oleh titik-titik.

Jadi misalnya dalam contoh Anda:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

jika Anda mengimpor moduleX(catatan: diimpor , tidak langsung dieksekusi), namanya akan menjadi package.subpackage1.moduleX. Jika Anda mengimpor moduleA, namanya akan menjadi package.moduleA. Namun, jika Anda langsung lari moduleX dari baris perintah, namanya akan menjadi __main__, dan jika Anda langsung lari moduleAdari baris perintah, namanya akan menjadi __main__. Ketika sebuah modul dijalankan sebagai skrip tingkat atas, ia kehilangan nama normalnya dan namanya adalah sebaliknya __main__.

Mengakses modul TIDAK melalui paketnya

Ada kerutan tambahan: nama modul tergantung pada apakah modul itu diimpor "langsung" dari direktori tempatnya, atau diimpor melalui paket. Ini hanya membuat perbedaan jika Anda menjalankan Python di direktori, dan mencoba mengimpor file di direktori yang sama (atau subdirektori dari itu). Misalnya, jika Anda memulai juru bahasa Python di direktori package/subpackage1dan kemudian melakukannya import moduleX, nama moduleXhanya akan moduleX, dan tidak package.subpackage1.moduleX. Ini karena Python menambahkan direktori saat ini ke jalur pencarian saat startup; jika ia menemukan modul yang akan diimpor di direktori saat ini, ia tidak akan tahu bahwa direktori itu adalah bagian dari paket, dan informasi paket tidak akan menjadi bagian dari nama modul.

Kasus khusus adalah jika Anda menjalankan interpreter secara interaktif (misalnya, cukup ketik pythondan mulai memasukkan kode Python dengan cepat). Dalam hal ini nama sesi interaktif itu adalah __main__.

Sekarang di sini adalah hal penting untuk pesan kesalahan Anda: jika nama modul tidak memiliki titik, itu tidak dianggap sebagai bagian dari paket . Tidak masalah di mana file itu sebenarnya ada di disk. Yang penting adalah apa namanya, dan namanya tergantung pada bagaimana Anda memuatnya.

Sekarang lihat kutipan yang Anda masukkan dalam pertanyaan Anda:

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

Impor relatif ...

Impor relatif menggunakan modul nama untuk menentukan di mana itu adalah dalam sebuah paket. Saat Anda menggunakan impor relatif seperti from .. import foo, titik-titik menunjukkan untuk meningkatkan beberapa level dalam hirarki paket. Misalnya, jika nama modul Anda saat ini adalah package.subpackage1.moduleX, maka ..moduleAitu berarti package.moduleA. Agar from .. importdapat bekerja, nama modul harus memiliki setidaknya titik sebanyak yang ada dalam importpernyataan.

... hanya relatif dalam satu paket

Namun, jika nama modul Anda __main__, itu tidak dianggap dalam sebuah paket. Namanya tidak memiliki titik, dan karena itu Anda tidak dapat menggunakan from .. importpernyataan di dalamnya. Jika Anda mencoba melakukannya, Anda akan mendapatkan kesalahan "relatif-impor dalam paket".

Skrip tidak dapat mengimpor relatif

Apa yang mungkin Anda lakukan adalah Anda mencoba menjalankan moduleXatau sejenisnya dari baris perintah. Ketika Anda melakukan ini, namanya ditetapkan ke __main__, yang berarti bahwa impor relatif di dalamnya akan gagal, karena namanya tidak mengungkapkan bahwa itu ada dalam paket. Perhatikan bahwa ini juga akan terjadi jika Anda menjalankan Python dari direktori yang sama di mana modul berada, dan kemudian mencoba mengimpor modul itu, karena, seperti yang dijelaskan di atas, Python akan menemukan modul dalam direktori saat ini "terlalu dini" tanpa disadari. bagian dari paket.

Juga ingat bahwa ketika Anda menjalankan juru bahasa interaktif, "nama" dari sesi interaktif itu selalu __main__. Dengan demikian Anda tidak dapat melakukan impor relatif langsung dari sesi interaktif . Impor relatif hanya untuk digunakan dalam file modul.

Dua solusi:

  1. Jika Anda benar-benar ingin menjalankan moduleXsecara langsung, tetapi Anda tetap ingin itu dianggap sebagai bagian dari paket, Anda dapat melakukannya python -m package.subpackage1.moduleX. The -mmemberitahu Python untuk memuatnya sebagai modul, tidak seperti naskah tingkat atas.

  2. Atau mungkin Anda sebenarnya tidak ingin menjalankan moduleX , Anda hanya ingin menjalankan beberapa skrip lain, misalnya myfile.py, yang menggunakan fungsi di dalamnya moduleX. Jika demikian, letakkan di myfile.py tempat lain - tidak di dalam packagedirektori - dan jalankan. Jika di dalam myfile.pyAnda melakukan hal-hal seperti from package.moduleA import spam, itu akan berfungsi dengan baik.

Catatan

  • Untuk salah satu dari solusi ini, direktori paket ( packagedalam contoh Anda) harus dapat diakses dari jalur pencarian modul Python ( sys.path). Jika tidak, Anda tidak akan dapat menggunakan apa pun dalam paket dengan andal.

  • Sejak Python 2.6, "nama" modul untuk keperluan resolusi paket ditentukan tidak hanya oleh __name__atributnya tetapi juga oleh __package__atributnya. Itu sebabnya saya menghindari menggunakan simbol eksplisit __name__untuk merujuk pada "nama" modul. Karena Python 2.6 modul "nama" modul secara efektif __package__ + '.' + __name__, atau hanya __name__jika __package__ada None.)

BrenBarn
sumber
62
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.Ini pada dasarnya mengganggu. Apa yang sulit dilihat dari direktori saat ini? Python harus mampu melakukan ini. Apakah ini diperbaiki dalam versi 3x?
7
@Stopforgetting myaccounts ...: PEP 366 menunjukkan cara kerjanya. Di dalam file, Anda dapat melakukan __package__ = 'package.subpackage1'atau sejenisnya. Maka file itu hanya akan selalu dianggap sebagai bagian dari paket itu bahkan jika dijalankan secara langsung. Jika Anda memiliki pertanyaan lain tentang __package__Anda, Anda mungkin ingin mengajukan pertanyaan terpisah karena kami akan keluar dari masalah pertanyaan awal Anda di sini.
BrenBarn
109
Ini harus menjadi jawaban untuk semua pertanyaan impor relatif Python. Ini harus ada dalam dokumen, bahkan.
edsioufi
10
Lihat python.org/dev/peps/pep-0366 - "Perhatikan bahwa boilerplate ini cukup hanya jika paket tingkat atas sudah dapat diakses melalui sys.path. Kode tambahan yang memanipulasi sys.path akan diperlukan untuk eksekusi langsung untuk bekerja tanpa paket tingkat atas sudah dapat diimpor. " - ini adalah bagian yang paling mengganggu bagi saya karena "kode tambahan" ini sebenarnya cukup panjang dan tidak dapat disimpan di tempat lain dalam paket agar mudah dijalankan.
Michael Scott Cuthbert
14
Jawaban ini saat ini tidak aktif pada beberapa detail penting tentang __name__dan sys.path. Secara khusus, dengan python -m pkg.mod, __name__diatur ke __main__, bukan pkg.mod; impor relatif diselesaikan menggunakan __package__daripada __name__dalam kasus ini. Juga, Python menambahkan direktori skrip daripada direktori saat ini sys.pathketika menjalankan python path/to/script.py; itu menambah direktori saat ini sys.pathketika menjalankan sebagian besar cara lain, termasuk python -m pkg.mod.
user2357112 mendukung Monica
42

Ini benar-benar masalah di dalam python. Asal mula kebingungan adalah bahwa orang secara keliru menganggap impor relatif sebagai jalur relatif yang bukan.

Misalnya ketika Anda menulis di faa.py :

from .. import foo

Ini memiliki makna hanya jika faa.py itu diidentifikasi dan dimuat oleh python, selama eksekusi, sebagai bagian dari paket. Dalam hal itu, nama modul untuk faa.py akan menjadi contoh some_packagename.faa . Jika file dimuat hanya karena berada di direktori saat ini, ketika python dijalankan, maka namanya tidak akan merujuk ke paket apa pun dan akhirnya impor relatif akan gagal.

Solusi sederhana untuk merujuk modul di direktori saat ini, adalah dengan menggunakan ini:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo
Rami Ka.
sumber
6
Solusi yang tepat adalah from __future__ import absolute_importdan memaksa pengguna untuk menggunakan kode Anda dengan benar ... sehingga Anda selalu dapat melakukannyafrom . import foo
Giacomo Alzetta
@ Giacomo: jawaban yang sangat tepat untuk masalah saya. Terima kasih!
Fábio
8

Berikut adalah resep umum, dimodifikasi agar sesuai sebagai contoh, yang saya gunakan saat ini untuk berurusan dengan pustaka Python yang ditulis sebagai paket, yang berisi file-file yang saling tergantung, di mana saya ingin dapat menguji sebagian dari mereka sedikit demi sedikit. Mari panggil ini lib.foodan katakan bahwa itu membutuhkan akses ke lib.fileAuntuk fungsi f1dan f2, dan lib.fileBuntuk kelas Class3.

Saya telah menyertakan beberapa printpanggilan untuk membantu menggambarkan cara kerjanya. Dalam praktiknya Anda ingin menghapusnya (dan mungkin juga from __future__ import print_functionbaris).

Contoh khusus ini terlalu sederhana untuk ditampilkan ketika kita benar-benar perlu memasukkan entri sys.path. (Lihat jawaban Lars untuk kasus di mana kami benar- benar membutuhkannya, ketika kami memiliki dua atau lebih direktori paket, dan kemudian kami menggunakan os.path.dirname(os.path.dirname(__file__))—tetapi tidak ada salahnya juga di sini.) Juga cukup aman untuk melakukan ini tanpa if _i in sys.pathuji. Namun, jika masing-masing file yang diimpor menyisipkan jalur yang sama — misalnya, jika keduanya fileAdan fileBingin mengimpor utilitas dari paket — ini sys.pathberulang kali dengan jalur yang sama, jadi ada baiknya if _i not in sys.pathdi dalam boilerplate.

from __future__ import print_function # only when showing how this works

if __package__:
    print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
    from .fileA import f1, f2
    from .fileB import Class3
else:
    print('Not a package; __name__ is {!r}'.format(__name__))
    # these next steps should be used only with care and if needed
    # (remove the sys.path manipulation for simple cases!)
    import os, sys
    _i = os.path.dirname(os.path.abspath(__file__))
    if _i not in sys.path:
        print('inserting {!r} into sys.path'.format(_i))
        sys.path.insert(0, _i)
    else:
        print('{!r} is already in sys.path'.format(_i))
    del _i # clean up global name space

    from fileA import f1, f2
    from fileB import Class3

... all the code as usual ...

if __name__ == '__main__':
    import doctest, sys
    ret = doctest.testmod()
    sys.exit(0 if ret.failed == 0 else 1)

Idenya di sini adalah ini (dan perhatikan bahwa semua ini berfungsi sama di python2.7 dan python 3.x):

  1. Jika dijalankan sebagai import libatau from lib import foosebagai paket reguler, impor dari kode biasa, __packageadalah libdan __name__sekarang lib.foo. Kami mengambil jalur kode pertama, mengimpor dari .fileA, dll.

  2. Jika dijalankan sebagai python lib/foo.py, __package__akan menjadi None dan __name__akan menjadi __main__.

    Kami mengambil jalur kode kedua. The libdirektori yang sudah akan berada di sys.pathsehingga tidak perlu untuk menambahkannya. Kami mengimpor dari fileA, dll.

  3. Jika dijalankan dalam libdirektori sebagai python foo.py, perilaku ini sama seperti untuk kasus 2.

  4. Jika dijalankan dalam libdirektori sebagai python -m foo, perilaku ini mirip dengan kasus 2 dan 3. Namun, jalur ke libdirektori tidak ada sys.path, jadi kami menambahkannya sebelum mengimpor. Hal yang sama berlaku jika kita menjalankan Python lalu import foo.

    (Karena . sudah di dalam sys.path, kita tidak benar-benar perlu menambahkan versi absolut dari path di sini. Di sinilah struktur paket bersarang yang lebih dalam, di mana kita ingin lakukan from ..otherlib.fileC import ..., membuat perbedaan. Jika Anda tidak melakukan ini, Anda dapat hilangkan semua sys.pathmanipulasi sepenuhnya.)

Catatan

Masih ada kekhasan. Jika Anda menjalankan semua ini dari luar:

$ python2 lib.foo

atau:

$ python3 lib.foo

perilaku tergantung pada isi lib/__init__.py. Jika itu ada dan kosong , semuanya baik-baik saja:

Package named 'lib'; __name__ is '__main__'

Tetapi jika lib/__init__.py itu sendiri impor routinesehingga dapat mengekspor routine.namelangsung lib.name, Anda mendapatkan:

$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'

Yaitu, modul akan diimpor dua kali, sekali melalui paket dan kemudian lagi __main__sehingga menjalankan mainkode Anda . Python 3.6 dan yang lebih baru memperingatkan tentang ini:

$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'

The peringatan baru, tapi perilaku memperingatkan-tentang tidak. Ini adalah bagian dari apa yang oleh beberapa orang disebut perangkap impor ganda . (Untuk perincian tambahan, lihat edisi 27487. ) Nick Coghlan mengatakan:

Jebakan berikutnya ini ada di semua versi Python saat ini, termasuk 3.3, dan dapat diringkas dalam pedoman umum berikut: "Jangan pernah menambahkan direktori paket, atau direktori apa pun di dalam paket, langsung ke jalur Python".

Perhatikan bahwa sementara kami melanggar aturan itu di sini, kami melakukannya hanya ketika file yang sedang dimuat tidak dimuat sebagai bagian dari paket, dan modifikasi kami dirancang khusus untuk memungkinkan kami mengakses file lain dalam paket itu. (Dan, seperti yang saya catat, kita mungkin tidak boleh melakukan ini sama sekali untuk paket tingkat tunggal.) Jika kita ingin menjadi ekstra bersih, kita mungkin menulis ulang ini sebagai, misalnya:

    import os, sys
    _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    if _i not in sys.path:
        sys.path.insert(0, _i)
    else:
        _i = None

    from sub.fileA import f1, f2
    from sub.fileB import Class3

    if _i:
        sys.path.remove(_i)
    del _i

Yaitu, kami memodifikasi sys.pathcukup lama untuk mencapai impor kami, lalu mengembalikannya seperti semula (menghapus satu salinan _ijika dan hanya jika kami menambahkan satu salinan _i).

torek
sumber
7

Jadi setelah memikirkan hal ini bersama dengan banyak orang lain, saya menemukan catatan yang diposting oleh Dorian B dalam artikel ini yang memecahkan masalah spesifik yang saya alami di mana saya akan mengembangkan modul dan kelas untuk digunakan dengan layanan web, tetapi saya juga ingin menjadi dapat mengujinya saat saya mengode, menggunakan fasilitas debugger di PyCharm. Untuk menjalankan tes di kelas mandiri, saya akan menyertakan yang berikut di akhir file kelas saya:

if __name__ == '__main__':
   # run test code here...

tetapi jika saya ingin mengimpor kelas atau modul lain dalam folder yang sama, saya kemudian harus mengubah semua pernyataan impor saya dari notasi relatif ke referensi lokal (yaitu menghapus titik (.)) Tetapi setelah membaca saran Dorian, saya mencoba nya ' satu-liner 'dan berhasil! Saya sekarang dapat menguji di PyCharm dan meninggalkan kode tes saya di tempat ketika saya menggunakan kelas di kelas lain yang diuji, atau ketika saya menggunakannya di layanan web saya!

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

Pernyataan if memeriksa untuk melihat apakah kita menjalankan modul ini sebagai utama atau jika sedang digunakan dalam modul lain yang sedang diuji sebagai utama . Mungkin ini jelas, tetapi saya menawarkan catatan ini di sini kalau-kalau ada orang lain yang frustrasi dengan masalah impor relatif di atas dapat memanfaatkannya.

Steve L.
sumber
1
Itu benar-benar menyelesaikannya. Tapi itu benar-benar jahat. Mengapa ini bukan perilaku default ?!
lo tolmencre
4

Berikut adalah satu solusi yang tidak akan saya rekomendasikan, tetapi mungkin berguna dalam beberapa situasi di mana modul tidak dihasilkan:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()
Federico
sumber
2

Saya memiliki masalah yang sama di mana saya tidak ingin mengubah jalur pencarian modul Python dan perlu memuat modul relatif dari skrip (terlepas dari "skrip tidak dapat mengimpor relatif dengan semua" seperti yang dijelaskan oleh BrenBarn dengan baik di atas).

Jadi saya menggunakan hack berikut. Sayangnya, itu bergantung pada impmodul yang menjadi usang sejak versi 3.4 yang akan dibatalkan importlib. (Apakah ini mungkin dengan importlibjuga? Saya tidak tahu.) Namun, peretasan bekerja untuk saat ini.

Contoh untuk mengakses anggota moduleXin subpackage1dari skrip yang berada di subpackage2folder:

#!/usr/bin/env python3

import inspect
import imp
import os

def get_script_dir(follow_symlinks=True):
    """
    Return directory of code defining this very function.
    Should work from a module as well as from a script.
    """
    script_path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        script_path = os.path.realpath(script_path)
    return os.path.dirname(script_path)

# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)

# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST

Pendekatan yang lebih bersih adalah memodifikasi sys.path yang digunakan untuk memuat modul seperti yang disebutkan oleh Federico.

#!/usr/bin/env python3

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
   sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *
Lars
sumber
Itu terlihat lebih baik ... sayang sekali masih mengharuskan Anda untuk menanamkan nama direktori induk dalam file ... mungkin itu dapat ditingkatkan dengan importlib. Mungkin importlib bahkan dapat monkeypatched untuk membuat impor relatif "hanya berfungsi" untuk kasus penggunaan sederhana. Saya akan mengambil celah itu.
Andrew Wagner
Saya menggunakan python 2.7.14. Apakah sesuatu seperti ini masih berfungsi?
user3474042
Saya baru saja menguji kedua pendekatan pada python 2.7.10 dan mereka bekerja dengan baik untuk saya. Jika faktanya, Anda tidak memiliki masalah modul imp usang di 2.7, jadi semuanya lebih baik.
Lars
2

__name__ perubahan tergantung pada apakah kode tersebut dijalankan di namespace global atau sebagai bagian dari modul yang diimpor.

Jika kode tidak berjalan di ruang global, __name__akan menjadi nama modul. Jika sedang berjalan di namespace global - misalnya, jika Anda mengetiknya di konsol, atau menjalankan modul sebagai skrip menggunakan python.exe yourscriptnamehere.pymaka __name__menjadi "__main__".

Anda akan melihat banyak kode python if __name__ == '__main__'digunakan untuk menguji apakah kode tersebut dijalankan dari namespace global - yang memungkinkan Anda memiliki modul yang berfungsi ganda sebagai skrip.

Apakah Anda mencoba melakukan impor ini dari konsol?

theodox
sumber
Ah, jadi Anda menyebutkan -m. Itu membuat modul Anda dieksekusi sebagai skrip - jika Anda menempelkan if __name__ == '__main__' di sana, Anda akan melihat bahwa itu adalah '__main__' karena -m. Cobalah hanya mengimpor modul Anda ke modul lain sehingga bukan tingkat atas ... yang seharusnya memungkinkan Anda untuk melakukan impor relatif
theodox
Saya mencoba melakukan impor ini dari konsol, dengan file yang aktif menjadi modul yang benar.
@Stopforgetting akun saya ...: Apa maksud Anda "file aktif"?
BrenBarn
Saya menggunakan Pyscripter. Saya berada di moduleX.py ketika saya menjalankan impor ini: dari .moduleY mengimpor spam dan dari. mengimpor ModuleY.
Tidak mengimpor .moduleY diikuti oleh moduleY.spam ()?
theodox
2

@ BrenBarn menjawab dengan mengatakan itu semua, tetapi jika Anda seperti saya mungkin perlu waktu untuk mengerti. Inilah kasus saya dan bagaimana jawaban @ BrenBarn berlaku untuk itu, mungkin ini akan membantu Anda.

Kasus

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

Menggunakan contoh yang kita kenal, dan menambahkan bahwa moduleX.py memiliki impor relatif ke ..moduleA. Mengingat bahwa saya mencoba menulis skrip pengujian di direktori subpackage1 yang mengimpor moduleX, tetapi kemudian mendapatkan kesalahan yang menakutkan yang dijelaskan oleh OP.

Larutan

Pindahkan skrip pengujian ke level yang sama dengan paket dan impor package.subpackage1.moduleX

Penjelasan

Seperti yang dijelaskan, impor relatif dibuat relatif terhadap nama saat ini. Ketika skrip pengujian saya mengimpor moduleX dari direktori yang sama, maka nama modul di dalam moduleX adalah moduleX. Ketika bertemu dengan impor relatif interpreter tidak dapat membuat cadangan hierarki paket karena sudah di atas

Ketika saya mengimpor moduleX dari atas, maka nama di dalam moduleX adalah package.subpackage1.moduleX dan impor relatif dapat ditemukan

Brad Dre
sumber
Berharap Anda bisa membimbing saya dalam hal ini. Di tautan berikut, jika Anda membuka Kasus 3, dikatakan solusi 1 tidak mungkin. Tolong bisakah Anda memeriksa ini dan beri tahu saya. Ini akan sangat membantu saya. chrisyeh96.github.io/2017/08/08/...
variabel
@tervariabel ada salah ketik di tautan dan saya tidak diizinkan mengedit. Melihat kasus 3 dan tidak mengikuti persis apa yang Anda maksud. Ketika saya mencoba contoh itu di python 2 tidak ada masalah yang membuat saya berpikir saya melewatkan sesuatu. Mungkin Anda harus memposting pertanyaan baru tetapi perlu memberikan contoh yang lebih jelas. Kasus 4 menyentuh apa yang saya bicarakan dalam jawaban saya di sini: Anda tidak dapat naik direktori untuk impor relatif KECUALI penerjemah dimulai di direktori induk
Brad Dre
Terima kasih saya mengacu pada python 3 dan di sini pertanyaan stackoverflow.com/questions/58577767/...
variabel
1

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

Menulis paket python kecil ke PyPi yang mungkin membantu pemirsa pertanyaan ini. Paket ini bertindak sebagai solusi jika seseorang ingin dapat menjalankan file python yang mengandung impor yang berisi paket tingkat atas dari dalam suatu paket / proyek tanpa langsung di direktori file impor. https://pypi.org/project/import-anywhere/

ec2604
sumber
-2

Untuk membuat Python tidak kembali kepada saya "Mencoba impor relatif dalam paket non". paket/

init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py

Kesalahan ini terjadi hanya jika Anda menerapkan impor relatif ke file induk. Misalnya file induk sudah kembali utama setelah Anda kode "cetak ( nama )" di moduleA.py. Jadi file INI sudah utamatidak dapat mengembalikan paket induk lebih lanjut. impor relatif diperlukan dalam file paket subpackage1 dan subpackage2 Anda dapat menggunakan ".." untuk merujuk ke direktori atau modul induk. Tetapi orangtua adalah jika sudah paket tingkat atas, ia tidak dapat melangkah lebih jauh di atas direktori induk (paket). File seperti itu di mana Anda menerapkan impor relatif ke orang tua hanya dapat bekerja dengan aplikasi impor absolut. Jika Anda akan menggunakan IMPOR MUTLAK DALAM PAKET ORANGTUA TIDAK ADA KESALAHAN yang akan terjadi karena python tahu siapa yang berada di tingkat atas paket bahkan jika file Anda berada dalam subkemasan karena konsep PYTHON PATH yang menentukan tingkat atas proyek.

Sakshi Jain
sumber