it-swarm-id.com

Bagaimana cara melakukan impor relatif dengan Python?

Bayangkan struktur direktori ini:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

Saya mengkode mod1, dan saya perlu mengimpor sesuatu dari mod2. Bagaimana saya harus melakukannya?

Saya mencoba from ..sub2 import mod2 tapi saya mendapatkan "Percobaan relatif impor dalam paket".

Saya mencari di Google tetapi hanya menemukan hacks "sys.path manipulasi". Apakah tidak ada cara yang bersih?


Sunting: semua __init__.py saya saat ini kosong

Sunting2: Saya mencoba melakukan ini karena sub2 berisi kelas yang dibagi di seluruh sub paket (sub1, subX, dll.).

Sunting3: Perilaku yang saya cari adalah sama seperti yang dijelaskan dalam PEP 366 (terima kasih John B)

487
Joril

Semua orang tampaknya ingin memberi tahu Anda apa yang harus Anda lakukan daripada hanya menjawab pertanyaan.

Masalahnya adalah Anda menjalankan modul sebagai '__main__' dengan melewatkan mod1.py sebagai argumen kepada penerjemah.

Dari PEP 328 :

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

Dalam Python 2.6, mereka menambahkan kemampuan untuk referensi modul relatif ke modul utama. PEP 366 menjelaskan perubahan.

Pembaruan : Menurut Nick Coghlan, alternatif yang disarankan adalah menjalankan modul di dalam paket menggunakan saklar -m.

306
John B
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Anda menjalankan python main.py.
  2. main.py tidak: import app.package_a.module_a
  3. module_a.py tidak import app.package_b.module_b

Atau 2 atau 3 dapat menggunakan: from app.package_a import module_a

Itu akan bekerja selama Anda memiliki app di PYTHONPATH Anda. main.py bisa di mana saja.

Jadi, Anda menulis setup.py untuk menyalin (menginstal) seluruh paket aplikasi dan subpackages ke folder python sistem target, dan main.py untuk menargetkan folder skrip sistem.

116
nosklo

Inilah solusi yang cocok untuk saya:

Saya melakukan impor relatif sebagai from ..sub2 import mod2 dan kemudian, jika saya ingin menjalankan mod1.py maka saya pergi ke direktori induk app dan menjalankan modul menggunakan saklar python -m sebagai python -m app.sub1.mod1.

Alasan sebenarnya mengapa masalah ini terjadi pada impor relatif, adalah impor relatif bekerja dengan mengambil properti __name__ modul. Jika modul sedang dijalankan secara langsung, maka __name__ diatur ke __main__ dan tidak berisi informasi tentang struktur paket. Dan, itu sebabnya python mengeluh tentang kesalahan relative import in non-package.

Jadi, dengan menggunakan -m switch Anda memberikan informasi struktur paket ke python, yang dengannya ia dapat menyelesaikan impor relatif dengan sukses.

Saya telah mengalami masalah ini berkali-kali saat melakukan impor relatif. Dan, setelah membaca semua jawaban sebelumnya, saya masih tidak dapat menemukan cara untuk menyelesaikannya, dengan cara yang bersih, tanpa perlu memasukkan kode boilerplate di semua file. (Meskipun beberapa komentar sangat membantu, terima kasih kepada @ncoghlan dan @XiongChiamiov)

Semoga ini membantu seseorang yang berjuang dengan masalah impor relatif, karena melalui PEP benar-benar tidak menyenangkan.

114
Pankaj

"Guido memandang skrip yang berjalan dalam suatu paket sebagai anti-pola" (ditolak PEP-3122 )

Saya telah menghabiskan banyak waktu untuk mencari solusi, membaca posting terkait di sini di Stack Overflow dan berkata pada diri sendiri "pasti ada cara yang lebih baik!". Sepertinya tidak ada.

46
lesnik

Ini diselesaikan 100%:

  • aplikasi/
    • main.py
  • pengaturan /
    • local_setings.py

Impor pengaturan/local_setting.py di app/main.py:

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

Saya menggunakan cuplikan ini untuk mengimpor modul dari jalur, semoga bisa membantu

24
iElectric

penjelasan jawaban nosklo's dengan contoh

catatan: semua file __init__.py kosong.

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

app/package_a/fun_a.py

def print_a():
    print 'This is a function in dir package_a'

app/package_b/fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

main.py

from app.package_b import fun_b
fun_b.print_b()

jika Anda menjalankan $ python main.py ia mengembalikan:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py tidak: from app.package_b import fun_b
  • fun_b.py melakukan from app.package_a.fun_a import print_a

jadi file dalam folder package_b digunakan file dalam folder package_a, yang adalah apa yang Anda inginkan. Kanan??

21
suhailvs

Sayangnya ini adalah hack sys.path, tetapi ia bekerja dengan cukup baik.

Saya mengalami masalah ini dengan lapisan lain: Saya sudah memiliki modul dengan nama yang ditentukan, tetapi itu adalah modul yang salah.

apa yang ingin saya lakukan adalah sebagai berikut (modul tempat saya bekerja adalah module3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

Perhatikan bahwa saya telah menginstal mymodule, tetapi dalam instalasi saya, saya tidak memiliki "mymodule1"

dan saya akan mendapatkan ImportError karena mencoba mengimpor dari modul yang diinstal.

Saya mencoba melakukan sys.path.append, dan itu tidak berhasil. Apa yang berhasil adalah sys.path.insert

if __== '__main__':
    sys.path.insert(0, '../..')

Semacam hack, tapi semuanya berhasil! Jadi perlu diingat, jika Anda ingin keputusan Anda menimpa jalur lain maka Anda perlu menggunakan sys.path.insert (0, pathname) untuk membuatnya berfungsi! Ini adalah titik yang sangat menyebalkan bagi saya, banyak orang mengatakan untuk menggunakan fungsi "append" untuk sys.path, tetapi itu tidak berfungsi jika Anda sudah memiliki modul yang ditentukan (saya menemukan perilaku yang sangat aneh)

11
Garrett Berg

Biarkan saya letakkan ini di sini untuk referensi saya sendiri. Saya tahu bahwa itu bukan kode Python yang baik, tetapi saya membutuhkan skrip untuk proyek yang sedang saya kerjakan dan saya ingin meletakkan skrip dalam direktori scripts.

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
10
milkypostman

Seperti yang dikatakan @EvgeniSergeev dalam komentar di OP, Anda dapat mengimpor kode dari file .py di lokasi arbitrer dengan:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Ini diambil dari this SO answer .

8
LondonRob

Lihatlah http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-import . Anda bisa melakukannya

from .mod1 import stuff
4
mossplix

Dari Python doc ,

Dalam Python 2.5, Anda bisa mengubah perilaku impor menjadi impor absolut menggunakan arahan from __future__ import absolute_import. Perilaku impor absolut ini akan menjadi default di versi yang akan datang (mungkin Python 2.7). Setelah impor absolut adalah default, import string akan selalu menemukan versi perpustakaan standar. Disarankan agar pengguna mulai menggunakan impor absolut sebanyak mungkin, jadi lebih baik untuk mulai menulis from pkg import string dalam kode Anda

2
jung rhew

Di atas apa yang dikatakan John B, sepertinya mengatur variabel __package__ akan membantu, alih-alih mengubah __main__ yang dapat mengacaukan hal-hal lain. Tetapi sejauh yang saya bisa uji, itu tidak sepenuhnya berfungsi sebagaimana mestinya.

Saya memiliki masalah yang sama dan PEP 328 atau 366 tidak menyelesaikan masalah sepenuhnya, karena keduanya, pada akhir hari, perlu kepala paket untuk dimasukkan dalam sys.path, sejauh yang saya bisa mengerti.

Saya juga harus menyebutkan bahwa saya tidak menemukan cara memformat string yang harus dimasukkan ke dalam variabel tersebut. Apakah "package_head.subfolder.module_name" atau apa?

1
Gabriel

Saya menemukan lebih mudah untuk mengatur variabel lingkungan "PYTHONPATH" ke folder teratas:

bash$ export PYTHONPATH=/PATH/TO/APP

kemudian:

import sub1.func1
#...more import

tentu saja, PYTHONPATH adalah "global", tetapi itu belum menimbulkan masalah bagi saya.

1
Andrew_1510