Milyarıncı kez göreli ithalat


722

Buraya daha önce geldim:

ve hızlı bir şekilde çözümü alacağımı düşündüğümde, bazıları SO'da, bazıları diğer sitelerde, kopyalamadığım birçok URL.

Sonsuza kadar yinelenen soru şudur: Windows 7, 32 bit Python 2.7.3 ile, bu "Paket olmayan pakete göreceli içe aktarma girişimi" iletisini nasıl çözebilirim? Paketin tam bir kopyasını pep-0328 üzerine inşa ettim:

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

İthalat konsoldan yapıldı.

Uygun modüllerinde spam ve yumurta adlı işlevler yaptım. Doğal olarak, işe yaramadı. Cevap görünüşte listelediğim 4. URL'de, ama hepsi benim için mezun. Ziyaret ettiğim URL'lerden birinde bu yanıt oluştu:

Göreli içe aktarmalar, modülün paket hiyerarşisindeki konumunu belirlemek için bir modülün name özniteliğini kullanır. Modülün adı herhangi bir paket bilgisi içermiyorsa (örn. 'Ana' olarak ayarlanmışsa), göreli içe aktarmalar, modülün dosya sisteminde nerede bulunduğuna bakılmaksızın modülün en üst düzey bir modülmiş gibi çözümlenir.

Yukarıdaki yanıt umut verici görünüyor, ama hepsi bana hiyeroglif. Peki sorum, Python'un bana "Paket dışı göreceli içe aktarma denemesi" nin geri dönmemesini nasıl sağlarım? sözde -m içeren bir cevabı var.

Birisi bana Python'un neden bu hata mesajını verdiğini, "paket dışı" ile ne anlama geldiğini, neden ve nasıl bir 'paket' tanımladığınızı ve bir anaokulunun anlaması için yeterince kolay bir şekilde ifade ettiği kesin cevabı söyleyebilir mi ?


5
Gösterdiğiniz dosyaları nasıl kullanmaya çalışıyorsunuz? Çalıştırdığınız kod nedir?
BrenBarn

Bkz. Python.org/dev/peps/pep-0328 . Yazımda açıkladığım paket biçimini kullandım. İnit .py dosyaları boş. moduleY.py sahiptir def spam(): pass, moduleA.py sahiptir def eggs(): pass. Bir çift ".something bir şey ithal" komutları yürütmeye çalıştım, ama işe yaramadı. Yine pep-0328'e bakınız.

6
Cevabımı gör. Hala ne yaptığınızı tam olarak açıklığa kavuşturmadınız, ancak from .something import somethingetkileşimli yorumlayıcıda yapmaya çalışıyorsanız , bu işe yaramaz. Bağıl ithalat, etkileşimli olarak değil, sadece modüller içinde kullanılabilir.
BrenBarn

105
Sadece "milyarlarca insan" - bu yorumdan itibaren 83.136 ok - bu soruyu araştırmak için ithalatta yeterli zorluk yaşıyor; yalnızca python ithalatının çoğu programcı olmasa da çoğu için karşı sezgisel olduğu sonucuna varabiliriz. Guido, belki de bunu kabul etmeli ve ithalat mekanizmasını yeniden tasarlamak için bir komite istemelisiniz. En azından, eğer x.py ve z.py aynı dizinde ise bu sözdiziminin çalışması gerekir. Yani x.py ".z import MyZebraClass from" ifadesini içeriyorsa x, main olarak çalıştırılıyorsa z EVEN içeri aktarmalıdır ! Neden bu kadar zor?
Steve L

5
Bu
konunun

Yanıtlar:


1046

Komut Dosyası ve Modül Karşılaştırması

İşte bir açıklama. Kısa sürüm, doğrudan bir Python dosyasını çalıştırmak ve bu dosyayı başka bir yerden içe aktarmak arasında büyük bir fark olmasıdır. Bir dosyanın hangi dizinde olduğunu bilmek, Python'un hangi paket içinde olduğunu düşündüğünü belirlemez. Bu, dosyayı Python'a nasıl yüklediğinize (çalıştırarak veya içe aktararak) bağlıdır.

Python dosyasını yüklemenin iki yolu vardır: en üst düzey komut dosyası olarak veya bir modül olarak. Bir dosyayı doğrudan yürütürseniz üst düzey komut dosyası olarak, örneğin python myfile.pykomut satırına yazarak yüklenir . Bunu yaparsanız python -m myfileveya importbaşka bir dosyanın içinde bir deyimle karşılaşıldığında yüklenirse modül olarak yüklenir . Bir kerede yalnızca bir üst düzey komut dosyası olabilir; üst düzey komut dosyası, işleri başlatmak için çalıştırdığınız Python dosyasıdır.

Adlandırma

Bir dosya yüklendiğinde, dosyaya bir ad verilir (bu dosya __name__özniteliğinde saklanır ). En üst düzey komut dosyası olarak yüklendiyse adı __main__. Bir modül olarak yüklendiyse, adı, önce parçası olan herhangi bir paket / alt paketin adlarıyla, noktalarla ayrılmış dosya adıdır.

Örneğin, örneğin:

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

içe aktardıysanız moduleX(not: içe aktarıldı , doğrudan yürütülmedi), adı olur package.subpackage1.moduleX. İçe aktardıysanız moduleA, adı olur package.moduleA. Ancak, doğrudan moduleX komut satırından çalıştırırsanız , adı bunun yerine olur __main__ve doğrudan moduleAkomut satırından çalıştırırsanız , adı olur __main__. Bir modül en üst düzey komut dosyası olarak çalıştırıldığında, normal adını kaybeder ve bunun yerine adı olur __main__.

Bir modüle içerdiği paket üzerinden erişme

Ek bir kırışıklık var: modülün adı, içinde bulunduğu dizinden "doğrudan" alınıp alınmadığına veya bir paket aracılığıyla alınıp alınmadığına bağlıdır. Bu yalnızca Python'u bir dizinde çalıştırırsanız ve aynı dizinde (veya alt dizininde) bir dosyayı içe aktarmaya çalışırsanız fark yaratır. Örneğin, dizinde Python yorumlayıcısını başlatır package/subpackage1ve sonra başlatırsanız import moduleXadı moduleXsadece olur moduleX, olmaz package.subpackage1.moduleX. Bunun nedeni, Python'un geçerli dizini başlangıçta arama yoluna eklemesidir; geçerli dizinde içe aktarılacak modülü bulursa, bu dizinin bir paketin parçası olduğunu bilmez ve paket bilgileri modül adının bir parçası olmaz.

Özel bir durum, yorumlayıcıyı etkileşimli olarak çalıştırmanızdır (örn., pythonPython kodunu anında yazın ve girmeye başlayın). Bu durumda, bu etkileşimli oturumun adıdır __main__.

Şimdi hata mesajınız için çok önemli olan şey: eğer bir modülün adında nokta yoksa bir paketin parçası sayılmaz . Dosyanın gerçekte diskte nerede olduğu önemli değil. Önemli olan tek şey adının ne olduğu ve adı nasıl yüklediğinize bağlı.

Şimdi sorunuza dahil ettiğiniz alıntıya bakın:

Göreli içe aktarmalar, modülün paket hiyerarşisindeki konumunu belirlemek için bir modülün name özniteliğini kullanır. Modülün adı herhangi bir paket bilgisi içermiyorsa (örn. 'Ana' olarak ayarlanmışsa), göreli içe aktarmalar, modülün dosya sisteminde nerede bulunduğuna bakılmaksızın modülün en üst düzey bir modülmiş gibi çözümlenir.

Göreli ithalat ...

Göreli içe aktarmalar , bir pakette nerede olduğunu belirlemek için modülün adını kullanır. Gibi göreli bir içe aktarma kullandığınızda from .. import foo, noktalar, paket hiyerarşisinde bazı düzeylerde yükseltmeyi belirtir. Mevcut modülün adıdır Örneğin, eğer package.subpackage1.moduleX, o zaman ..moduleAanlamına gelecektir package.moduleA. A'nın from .. importçalışması için modül adının importifadede yer alan en az sayıda noktaya sahip olması gerekir .

... sadece bir pakette göreceli

Ancak, modülünüzün adı __main__bir pakette olduğu kabul edilmez. Adında nokta yoktur ve bu nedenle from .. importiçinde ifadeler kullanamazsınız . Bunu yapmaya çalışırsanız, "paket dışı göreli alma" hatası alırsınız.

Komut dosyaları göreli olarak içe aktarılamaz

Muhtemelen moduleX, komut satırından çalıştırmayı denediğiniz veya benzer bir şeydir. Bunu yaptığınızda, adı olarak ayarlandı; bu __main__, içindeki göreli içe aktarma işleminin başarısız olacağı anlamına gelir, çünkü adı bir pakette olduğunu göstermez. Bunun, Python'u bir modülün bulunduğu dizinden çalıştırırsanız ve daha sonra bu modülü içe aktarmaya çalışırsanız da gerçekleşeceğini unutmayın, çünkü yukarıda açıklandığı gibi Python, geçerli dizinde modülü "çok erken" olduğunu fark etmeden bulacaktır. paketin bir parçası.

Ayrıca, etkileşimli yorumlayıcıyı çalıştırdığınızda, söz konusu etkileşimli oturumun "adı" nın her zaman olduğunu unutmayın __main__. Böylece , doğrudan etkileşimli bir oturumdan göreli içe aktarma yapamazsınız . Göreli ithalat sadece modül dosyalarında kullanım içindir.

İki çözüm:

  1. Gerçekten moduleXdoğrudan çalıştırmak istiyorsanız , ancak yine de bir paketin parçası olarak kabul edilmesini istiyorsanız, bunu yapabilirsiniz python -m package.subpackage1.moduleX. -mDeğil üst düzey komut dosyası gibi bir modül olarak yüklemek için Python söyler.

  2. Ya da belki de aslında istemiyorum çalıştırmak moduleX sadece diğer bazı komut dosyasını çalıştırmak söylemek istiyorum, myfile.pybu kullanımlar içinde işlevlerini moduleX. Bu durumda, dizinin içine değil - myfile.py başka bir yere koyun ve çalıştırın. Eğer içinizde gibi şeyler yaparsanız , iyi çalışır.packagemyfile.pyfrom package.moduleA import spam

notlar

  • Bu çözümlerden herhangi biri için paket dizinine ( packageörneğin) Python modülü arama yolundan ( sys.path) erişilebilir olmalıdır . Değilse, paketteki hiçbir şeyi güvenilir bir şekilde kullanamazsınız.

  • Python 2.6'dan bu yana, paket çözümleme amaçlı modülün "adı" yalnızca __name__niteliklerine göre değil , özelliğine göre de belirlenir __package__. Bu yüzden __name__modülün "adına" atıfta bulunmak için açık sembolü kullanmaktan kaçınıyorum . Python 2.6 Bir modülün "ad" etkili bir şekilde olduğu için __package__ + '.' + __name__, ya da sadece __name__eğer __package__olduğunu None.)


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.Bu temelde rahatsız edicidir. Geçerli dizine bakmanın zorluğu nedir? Python bunu yapabilmelidir. Bu, sürüm 3x'te düzeltildi mi?

7
@Stopforgettingmyaccounts ...: PEP 366 bunun nasıl çalıştığını gösterir. Bir dosyanın içinde __package__ = 'package.subpackage1'veya benzerlerini yapabilirsiniz. Sonra sadece bu dosya olacak hep doğrudan işletilen bile o paketin parçası olarak kabul. Hakkında başka sorularınız varsa, __package__burada orijinal sorunuzun sorununu çözdüğümüz için ayrı bir soru sormak isteyebilirsiniz.
BrenBarn

109
Bu, tüm Python göreli ithalat sorularının cevabı olmalıdır. Bu dokümanlarda bile olmalı.
edsioufi

10
Bkz. Python.org/dev/peps/pep-0366 - "Bu ortak plakaya yalnızca üst düzey pakete zaten sys.path üzerinden erişilebiliyorsa yeterli olduğunu unutmayın. Doğrudan yürütme için sys.path'i işleyen ek kod gerekli olacaktır üst düzey paket olmadan da çalışabilir hale getirmek. " - Bu "ek kod" aslında oldukça uzun ve kolayca çalıştırmak için paket içinde başka bir yerde saklanamaz çünkü bu benim için en rahatsız edici bit.
Michael Scott Cuthbert

14
Bu yanıt şu anda __name__ve ile ilgili birkaç önemli ayrıntıda kapalıdır sys.path. Özellikle, ile python -m pkg.mod, __name__ayarlanır __main__, değil pkg.mod; göreli ithalatlar bu durumdan __package__ziyade çözümlenir __name__. Ayrıca, Python sys.pathçalıştırılırken geçerli dizin yerine komut dosyasının dizinini ekler python path/to/script.py; sys.pathdahil olmak üzere diğer birçok yolu çalıştırırken geçerli dizini ekler python -m pkg.mod.
user2357112 Monica

43

Bu gerçekten python içinde bir problem. Karışıklığın kaynağı, insanların göreceli içe aktarmayı yanlışlıkla yol göreceli olarak görmemesidir.

Örneğin faa.py ile yazdığınızda :

from .. import foo

Bunun yalnızca faa.py , python tarafından yürütme sırasında bir paketin parçası olarak tanımlanması ve yüklenmesi durumunda bir anlamı vardır . Bu durumda, modülün ismi için faa.py örnek olurdu some_packagename.faa . Dosya sadece geçerli dizinde olduğu için yüklenmişse, python çalıştırıldığında, adı herhangi bir pakete başvurmaz ve sonunda göreceli içe aktarma başarısız olur.

Geçerli dizindeki modüllere başvurmak için basit bir çözüm, bunu kullanmaktır:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo

7
Doğru çözüm from __future__ import absolute_importve kullanıcıyı kodunuzu doğru bir şekilde kullanmaya zorlar ... böylece her zaman yapabilirsinizfrom . import foo
Giacomo Alzetta

1
@ Giacomo: sorunum için kesinlikle doğru cevap. Teşekkürler!
Fábio

8

İşte bir örnek olarak uyacak şekilde değiştirilmiş, şu anda paketler halinde yazılmış Python kütüphaneleri ile uğraşmak için kullandığım, bunların parça parça parçalarını test edebilmek için birbirine bağlı dosyalar içeren genel bir tarif. En bu diyelim lib.foove onu erişmesi gerekir söylemek lib.fileAfonksiyonlar için f1ve f2ve lib.fileBsınıf için Class3.

Bunun printnasıl çalıştığını göstermek için birkaç çağrı ekledim. Pratikte onları (ve belki de from __future__ import print_functionçizgiyi) kaldırmak istersiniz .

Bu özel örnek, gerçekten bir girdi eklememiz gerektiğinde göstermek için çok basittir sys.path. (Bkz Lars'ın cevabı biz dava için do biz paket dizinlerinin iki veya daha fazla düzeyleri, sonra kullandığınızda, ihtiyaç os.path.dirname(os.path.dirname(__file__))gerçekten değil -ama zarar ya burada.) O olmadan bunu güvenli yeterli de var if _i in sys.pathÖlçek. Ancak, içe aktarılan her dosya aynı yolu eklerse (örneğin, her ikisi de fileAve fileByardımcı programları paketten içe aktarmak istiyorsanız), bu sys.pathaynı yolla birçok kez toplanır , bu nedenle if _i not in sys.pathkazan plakasında olması güzeldir .

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)

Buradaki fikir şudur (ve bunların hepsinin python2.7 ve python 3.x'de aynı şekilde işlev gördüğüne dikkat edin):

  1. Olarak çalıştırırsanız import libveya from lib import foosıradan koddan düzenli paket içe aktarma işlevi, __packageolduğu libve __name__olduğu lib.foo. İlk kod yolunu alıyoruz .fileA.

  2. Olarak çalıştır python lib/foo.py, __package__Hiçbiri ve __name__olacaktır __main__.

    İkinci kod yolunu alıyoruz. libDizin zaten olacak sys.patheklemek için gerek yoktur bu yüzden. Biz ithalat fileA, vb.

  3. libDizinde olduğu gibi çalıştırılırsa python foo.py, davranış durum 2 ile aynıdır.

  4. libDizin içinde olduğu gibi çalıştırılırsa python -m foo, davranış durum 2 ve 3'e benzer. Ancak, libdizinin yolu girilmez sys.path, bu yüzden içe aktarmadan önce ekleriz. Aynı şey Python ve sonra çalıştırıldığında da geçerlidir import foo.

    (İçinde . olduğu için sys.path, yolun mutlak sürümünü buraya eklememiz gerekmiyor. Burası, yapmak istediğimiz daha derin bir paket iç içe geçmiş yapının from ..otherlib.fileC import ...fark yarattığı yerdir . Bunu yapmıyorsanız, tüm sys.pathmanipülasyonları tamamen atlayın .)

notlar

Hala bir tuhaflık var. Tüm bunları dışarıdan çalıştırırsanız:

$ python2 lib.foo

veya:

$ python3 lib.foo

davranış içeriğine bağlıdır lib/__init__.py. Varsa ve boşsa , her şey yolundadır:

Package named 'lib'; __name__ is '__main__'

Ancak lib/__init__.py kendisi doğrudan olarak routinedışa aktarılabilmesi için içe aktarırsa , şunları elde edersiniz:routine.namelib.name

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

Yani, modül iki kez içe aktarılır, bir kez paket üzerinden ve daha sonra tekrar kodunuzu __main__çalıştırır main. Python 3.6 ve üzeri bu konuda uyarır:

$ 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__'

Uyarı yeni olmakla uyardı-hakkında davranış değildir. Bazılarının çift ​​içe aktarma tuzağı dediği şeyin bir parçasıdır . (Ek ayrıntılar için 27487 numaralı konuya bakın .) Nick Coghlan diyor ki:

Bu sonraki tuzak, Python'un 3.3 dahil olmak üzere tüm geçerli sürümlerinde bulunur ve aşağıdaki genel kılavuzda özetlenebilir: "Asla bir paket dizini veya paketin içindeki herhangi bir dizini doğrudan Python yoluna ekleme".

Not biz burada bu kuralı ihlal ederken, bunu yapmak sadece dosya olduğu yüklenirken zaman değil bir paketin parçası olarak yüklenen ve bizim değişiklikleri spesifik bize bu paketteki diğer dosyalara erişmek için izin vermek için tasarlanmıştır. (Ve belirttiğim gibi, muhtemelen tek seviyeli paketler için bunu hiç yapmamalıyız.) Ekstra temiz olmak istiyorsak, bunu şöyle yeniden yazabiliriz, örneğin:

    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

Yani, sys.pathithalatımızı gerçekleştirecek kadar uzun süre değiştiririz , sonra olduğu gibi geri koyarız (bir kopyasını _iyalnızca bir kopyasını eklediğimizde silme _i).


7

Bu konuyu başkaları ile birlikte ele aldıktan sonra , bu yazıda Dorian B tarafından yayınlanan ve bir web servisiyle kullanmak için modüller ve sınıflar geliştireceğim yerde yaşadığım problemi çözen bir notla karşılaştım , ama aynı zamanda PyCharm'daki hata ayıklayıcı olanaklarını kullanarak kodlama yaptığımda bunları test edebiliyorum. Bağımsız bir sınıfta testleri çalıştırmak için, sınıf dosyamın sonuna aşağıdakileri eklerdim:

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

ancak aynı klasördeki diğer sınıfları veya modülleri içe aktarmak istersem, tüm içe aktarma ifadelerimi göreli gösterimden yerel referanslara değiştirmek zorunda kalırdım (yani noktayı (.) kaldırın). Ancak Dorian'ın önerisini okuduktan sonra ' tek astarlı 've işe yaradı! Şimdi PyCharm'da test yapabilir ve sınıfı test altındaki başka bir sınıfta kullandığımda veya web hizmetimde kullandığımda test kodumu yerinde bırakabilirim!

# 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

İfadesi çeklerinin olarak bu modülü çalıştırıyorsanız olmadığını görmek için ise ana veya o kadar test ediliyor başka modülde kullanılıyorsa, bu ana . Belki de bu açıktır, ancak yukarıdaki göreceli ithalat sorunlarından ötürü hayal kırıklığına uğramış herkesin yararlanabilmesi durumunda bu notu burada sunuyorum.


1
Aslında bunu çözer. Ama çok kötü. Neden bu varsayılan davranış değil ?!
lo tolmencre

4

İşte size tavsiye etmem, ancak modüllerin basitçe oluşturulmadığı bazı durumlarda yararlı olabilecek bir çözüm:

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()

2

Ben Python modül arama yolunu değiştirmek istemiyordu benzer bir sorun vardı ve nispeten bir komut dosyası bir modül yüklemek için gerekli (rağmen "komut dosyaları tüm göreli alabilirsiniz ithalat" BrenBarn yukarıda güzel açıklandığı gibi).

Bu yüzden aşağıdaki hack'i kullandım. Ne yazık ki, imp3.4 sürümünden yana bırakılacağı için kullanımdan kaldırılan modüle güveniyor importlib. (Bu da mümkün mü importlib? Bilmiyorum.) Yine de, kesmek şimdilik çalışıyor.

Üyelerini erişim için örnek moduleXiçinde subpackage1ikamet eden bir komut dosyasından subpackage2klasöre:

#!/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

Daha temiz bir yaklaşım, Federico tarafından belirtildiği gibi modüllerin yüklenmesi için kullanılan sys.path'i değiştirmek gibi görünüyor.

#!/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 *

Daha iyi görünüyor ... çok kötü, yine de üst dizinin adını dosyaya gömmenizi gerektirir ... belki de importlib ile geliştirilebilir. Belki importlib, basit kullanım durumları için göreceli içe aktarmayı "sadece çalışır" hale getirmek için bile kullanılabilir. Ben bir çatlak alacağım.
Andrew Wagner

Yine de python 2.7.14 kullanıyorum. Böyle bir şey hala çalışır mı?
user3474042

Python 2.7.10'da her iki yaklaşımı da test ettim ve benim için iyi çalıştılar. Eğer gerçekte, 2.7'de kullanımdan kaldırılmış bir imp modülü probleminiz yok, bu yüzden daha iyi.
Lars

2

__name__ söz konusu kodun genel ad alanında mı yoksa içe aktarılan bir modülün parçası olarak mı çalıştığına bağlı olarak değişir.

Kod global alanda çalışmıyorsa __name__, modülün adı olacaktır. Genel ad alanında çalışıyorsa - örneğin, bir konsola yazarsanız veya modülü komut dosyası olarak çalıştırırsanız, python.exe yourscriptnamehere.pyo zaman __name__olur "__main__".

if __name__ == '__main__'Kodun genel ad alanından çalıştırılıp çalıştırılmadığını test etmek için kullanılan bir çok python kodu göreceksiniz - bu da bir komut dosyası olarak iki katına çıkan bir modüle sahip olmanızı sağlar.

Bu ithalatı konsoldan yapmaya çalıştınız mı?


Ah, yani -m. Bu, modülünüzün bir komut dosyası olarak yürütülmesini sağlar - eğer bir if __name__ == '__main__' yapıştırırsanız, -m nedeniyle bunun '__main__' olduğunu görmelisiniz. Modülünüzü başka bir modüle almayı deneyin, böylece en üst seviye değil ... göreceli ithalat yapmanıza izin vermelidir
theodox 4

Konsoldan bu içe aktarmaları yapmaya çalıştım, aktif dosya doğru modül.

@Stopforgettingmyaccounts ...: "Aktif dosya" ne demek?
BrenBarn

Pyscripter kullanıyorum. Bu içe aktarımları çalıştırdığımda moduleX.py'deydim: .moduleY import spam ve from. içe aktarma ModülüY.

.ModuleY dosyasını ve ardından moduleY.spam () öğesini almıyor musunuz?
Theodox

2

@ BrenBarn'ın cevabı her şeyi söylüyor, ama eğer benim gibiyseniz anlamak biraz zaman alabilir. İşte benim durumum ve @ BrenBarn'ın cevabı bunun için nasıl geçerli, belki size yardımcı olacaktır.

Durum

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

Tanıdık örneğimizi kullanarak modülX.py'nin ..moduleA öğesine göreli bir içe aktarımı olduğunu ekleyin. ModülX'i ithal eden subpackage1 dizinine bir test komut dosyası yazmayı denedim, ancak OP tarafından açıklanan korkunç hatayı aldım.

Çözüm

Test komut dosyasını paketle aynı düzeye taşıyın ve package.subpackage1.moduleX dosyasını içe aktarın

açıklama

Açıklandığı gibi, mevcut isme göre göreli ithalatlar yapılır. Test komut dosyam moduleX'i aynı dizinden içe aktardığında, moduleX içindeki modül adı moduleX olur. Göreceli bir içe aktarmayla karşılaştığında yorumlayıcı, zaten üstte olduğu için paket hiyerarşisini yedekleyemez

ModuleX'i yukarıdan içe aktardığımda, moduleX içindeki ad paket.subpackage1.moduleX ve göreceli içe aktarma bulunabilir


Umarım bana bu konuda rehberlik edebilirsin. Aşağıdaki bağlantıda, Durum 3'e giderseniz, çözüm 1'in mümkün olmadığını söylüyor. Lütfen bunu kontrol edip bana bildirin. Bana çok yardımcı olacak. chrisyeh96.github.io/2017/08/08/…
değişken

@variable Bağlantıda bir yazım hatası var ve düzenlememe izin verilmiyor. Durum 3'e baktım ve tam olarak ne elde ettiğinizi takip etmediniz. Bu örneği python 2'de denediğimde, bir şeyleri kaçırdığımı düşündüren hiçbir sorun yoktu. Belki yeni bir soru göndermelisiniz, ancak daha net bir örnek vermelisiniz. Durum 4 burada cevabımda bahsettiğim konuya değiniyor: göreceli içe aktarma için bir dizine çıkamazsınız Tercüman bir üst dizinde
Brad Dre

Teşekkürler python 3'e atıfta bulunuyorum ve burada şu soru stackoverflow.com/questions/58577767/…
değişken

1

Göreli içe aktarmalar, modülün paket hiyerarşisindeki konumunu belirlemek için bir modülün name özniteliğini kullanır. Modülün adı herhangi bir paket bilgisi içermiyorsa (örn. 'Ana' olarak ayarlanmışsa), göreli içe aktarmalar, modülün dosya sisteminde nerede bulunduğuna bakılmaksızın modülün en üst düzey bir modülmiş gibi çözümlenir.

PyPi'ye bu sorunun izleyicilerine yardımcı olabilecek küçük bir python paketi yazdı. Üst düzey paketleri içeren ithalatları içeren python dosyalarını doğrudan içe aktarılan dosyanın dizininde bulunmadan bir paket / proje içinden çalıştırmak istiyorsa, paket geçici çözüm olarak çalışır. https://pypi.org/project/import-anywhere/


-2

Python bana dönmedi yapmak için "paket dışı göreceli içe aktarma girişimi". paket /

init .py altpaket1 / init .py modülX.py modülY.py altpaket2 / init .py modülZ.py modülA.py

Bu hata, yalnızca üst dosyaya göreli içe aktarma uyguluyorsanız oluşur. Örneğin , modül dosyası "print ( name )" kodlandıktan sonra ana dosya zaten main değerini döndürür. Bu nedenle BU dosya zaten mainüst paketi daha sonra iade edemez. üst dizine veya modüle başvurmak için ".." kullanabilirsiniz alt paket paket ve alt paket2 dosyalarda göreli ithalat gereklidir.Ancak üst paket zaten üst düzey paket ise o üst dizinin (paket) daha ileri gidemez. Ebeveynlere göreli içe aktarma uyguladığınız bu tür dosyalar yalnızca mutlak içe aktarma uygulamasıyla çalışabilir. AİLE PAKETİNDE ABSOLUTE İTHALATI kullanacaksanız, projenin en üst seviyesini tanımlayan PYTHON PATH kavramı nedeniyle dosyanız alt paketlerde olsa bile python'un kimin en üst düzeyde olduğunu bildiği gibi HİÇBİR HATA gelmeyecektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.