__İnit__.py ile bile "Paket dışı göreceli içe aktarma girişimi" nasıl düzeltilir?


744

Aşağıdaki dizin yapısı ile PEP 328'i takip etmeye çalışıyorum :

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

İçinde core_test.pyaşağıdaki ithalat beyanı var

from ..components.core import GameLoopEvents

Ancak, çalıştırdığımda, aşağıdaki hatayı alıyorum:

tests$ python core_test.py 
Traceback (most recent call last):
  File "core_test.py", line 3, in <module>
    from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package

Etrafta arama yaparken " göreli yol __init__.py ile bile çalışmıyor " ve " Bir modülü göreceli yoldan içe aktar " buldum ama yardımcı olmadı.

Burada özlediğim bir şey var mı?


17
Ben de çok yapılandırma çeşitli yollarla tarafından karıştı unittestben yazdım bu yüzden, projeler oldukça kapsamlı örnek proje göreli modülleri ve mutlak ithalatın kapakları derin yuvalama (burada iş yapması ve yapmaması) a içinden ve göreli ve mutlak referans paketin yanı sıra sınıfların tek, çift ve paket düzeyinde içe aktarılması. Net şeyler yardımcı oldu sağ benim için!
cod3monk3y

1
Testlerini çalıştıramadım. no module named myimports.fooOnları çalıştırdığımda almaya devam et .
Blairg23

@ Blairg23 Sanırım amaçlanan çağırma örneğin cdiçine girmek PyImportsve koşmak python -m unittest tests.test_abs.
Ocak'ta duozmo

7
Gene ile aynı fikirdeyim. Keşke ithalat sürecinin hata ayıklaması için biraz daha faydalı bir mekanizma olmasını dilerdim. Benim durumumda, aynı dizinde iki dosya var. Bir dosyayı diğer dosyaya aktarmaya çalışıyorum. Bu dizinde bir init .py dosyası varsa , paket dışı hatada bir ValueError: göreli içe aktarma girişimi alıyorum. Ben kaldırırsanız init .py dosyasını, sonra bir hata 'NAME' hatası adlı herhangi modül olsun.
Kullanıcı1928764

Benim durumumda, aynı dizinde iki dosya var. Bir dosyayı diğer dosyaya aktarmaya çalışıyorum. Bu dizinde bir init .py dosyası varsa , paket dışı hatada bir ValueError: göreli içe aktarma girişimi alıyorum. Ben kaldırırsanız init .py dosyasını, sonra bir hata 'NAME' hatası adlı herhangi modül olsun. Gerçekten sinir bozucu olan, bu çalışmayı yaptım ve sonra PYTHONPATH'ı bir şeye ayarlayan .bashrc dosyasını silerek kendimi ayağımdan vurdum ve şimdi çalışmıyor.
user1928764

Yanıtlar:


443

Evet. Paket olarak kullanmıyorsunuz.

python -m pkg.tests.core_test

51
Bir gotcha: Sonunda '.py' olmadığını unutmayın!
Mindthief

497
Ben de downvoters değilim, ama bu soru ve cevap popülerlik göz önüne alındığında, bu biraz daha ayrıntılı kullanabilirsiniz hissediyorum . Yukarıdaki kabuk komutunu yürütmek için __init__.pyhangi __package__dizinden , tümüyle aşağıya ihtiyacınız olduğu gerçeği ve -boding hile (aşağıda BrenBarn tarafından açıklanmaktadır) gibi yürütülebilir komut dosyaları (örneğin bir shebang ve yapıyor ./my_script.py) Unix kabuk hiç yararlı olacaktır. Tüm bu konu hakkında kısa ve anlaşılır belgeler bulmam veya bulmam benim için oldukça zor oldu.
Mark Amery

16
Not: pkgbu satırı CLI'den çağırdığınız noktada dizinin dışında olmanız gerekir . Sonra beklendiği gibi çalışmalıdır. Eğer içerideyseniz pkgve ararsanız python -m tests.core_test, çalışmaz. En azından benim için olmadı.
Blairg23

94
Cidden, cevabında neler olduğunu açıklayabilir misin?
Pinokyo

18
@MarkAmery Neredeyse tüm bunların nasıl çalıştığını görmek için aklımı kaybettim, göreli ithalatı dosya içeren py dosyaları ile alt dizinleri olan bir proje içinde __init__.pyhenüz ValueError: Attempted relative import in non-packagehata almaya devam ediyor . Sonunda tüm bunların nasıl çalıştığını açık İngilizce olarak anlatmak için bir yere, gerçekten iyi para ödeyeceğim.
AdjunctProfessorFalcon

635

Ignacio Vazquez-Abrams'ın cevabını detaylandırmak için :

Python içe aktarma mekanizması __name__geçerli dosyaya göre çalışır. Bir dosyayı doğrudan yürüttüğünüzde, normal adı yoktur, "__main__"bunun yerine adı vardır. Yani göreli ithalatlar işe yaramıyor.

Igancio'nun önerdiği gibi, -mseçeneği kullanarak yürütebilirsiniz . Paketinizin komut dosyası olarak çalıştırılması gereken bir parçanız varsa, __package__bu dosyaya paket hiyerarşisinde hangi isme sahip olması gerektiğini söylemek için özniteliği de kullanabilirsiniz .

Ayrıntılar için http://www.python.org/dev/peps/pep-0366/ adresine bakın.


55
Beni koşamam gerçekleştirmek için bir süre aldı python -m core_testiçinden testso ebeveynden olmak zorunda, yoksa yola ebeveyni eklemek zorunda - alt dizin.
Aram Kocharyan

3
@DannyStaple: Tam olarak değil. __package__Yürütülebilir komut dosyası dosyalarının aynı paket içindeki diğer modülleri nispeten içe aktarmasını sağlamak için kullanabilirsiniz . "Bütün sistem" den nispeten ithal etmenin bir yolu yoktur. Bunu neden yapmak istediğinden bile emin değilim.
BrenBarn

2
Yani eğer __package__simge "parent.child" o zaman "parent.other_child" ithal etmek mümkün olurdu ayarlanır. Belki de çok iyi ifade etmedim.
Danny Staple

5
@DannyStaple: Peki, nasıl çalıştığı bağlantılı belgelerde açıklanmıştır. Bir komut dosyası varsa script.pypakette pack.subpack, o zaman var ayarı __package__için pack.subpackyapmanız izin verir from ..module import somethingithal şeye pack.module. Belgelerin dediği gibi, sistem yolunda hala en üst düzey pakete sahip olmanız gerektiğini unutmayın. Bu zaten içe aktarılan modüller için işler. Tek __package__yapmanız gereken bu davranışı doğrudan yürütülen komut dosyaları için de kullanmaktır.
BrenBarn

3
__package__Doğrudan yürütülen komut dosyasında kullanıyorum ama ne yazık ki, aşağıdaki hatayı alıyorum: "Ana modül 'xxx' yüklü değil, göreceli ithalat gerçekleştiremez"
mononoke

202

import components.coreGeçerli dizini şu konuma eklerseniz doğrudan kullanabilirsiniz sys.path:

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))

35
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))Bu da işe yarayacak
ajay

26
from os import syshile gibi görünüyor :)
uçan koyun

3
@Piotr: Daha iyi düşünülebilir, çünkü neyin eklendiğini biraz daha net gösterir sys.path- geçerli dosyanın bulunduğu dizinin üst öğesi.
martineau

8
@flyingsheep: Katılıyorum, sadece normal bir kullanırım import sys, os.path as path.
martineau

10
Bilginize, bir ipython defterine şu kullanmayı ben bu cevabı adapte: import os; os.sys.path.append(os.path.dirname(os.path.abspath('.'))). Daha sonra import components.core, dizüstü bilgisayarın üst dizininden istendiği gibi içe aktarılan bir düz benim için çalışıyor.
Yarış Tadpole

195

Komut dosyanızı nasıl başlatmak istediğinize bağlıdır.

UnitTest'inizi komut satırından klasik bir şekilde başlatmak istiyorsanız , yani:

python tests/core_test.py

Daha sonra, bu durumda 'bileşenler' ve 'testler' kardeşler klasörü olduğundan, sys.path modülünün insert veya append yöntemini kullanarak ilgili modülü içe aktarabilirsiniz . Gibi bir şey:

import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents

Aksi takdirde, komut dosyanızı '-m' argümanıyla başlatabilirsiniz (bu durumda bir paketten bahsettiğimizi ve bu nedenle '.py' uzantısını vermemeniz gerektiğini unutmayın ), yani:

python -m pkg.tests.core_test

Böyle bir durumda, göreli içe aktarmayı yaptığınız gibi kullanabilirsiniz:

from ..components.core import GameLoopEvents

Sonunda iki yaklaşımı karıştırabilirsiniz, böylece komut dosyanız nasıl adlandırılırsa çalışsın çalışacaktır. Örneğin:

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        from components.core import GameLoopEvents
    else:
        from ..components.core import GameLoopEvents

3
Hata ayıklama için pdb kullanmaya çalışıyorsam ne yapmalıyım? çünkü python -m pdb myscript.pyhata ayıklama oturumunu başlatmak için kullanılır .
danny

1
@dannynjust - 2 ana modülünüz olmadığından bu iyi bir soru. Genellikle hata ayıklama sırasında, hata ayıklamaya başlamak istediğim ilk noktada manuel olarak hata ayıklayıcıya bırakmayı tercih ederim. Bunu import pdb; pdb.set_trace()koda (satır içi) bir a ekleyerek yapabilirsiniz .
mgilson

3
insertBunun yerine kullanmak daha mı iyi append? Yani,sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
SparkAndShine

2
Insert kullanmak, yerel paket adlarının kurulu paketlere göre öncelikli olduğu göreli içe aktarma semantiği için daha iyi bir eştir. Özellikle testler için, yüklü sürümü değil, genellikle yerel sürümü test etmek istersiniz (test altyapınız test edilen kodu yüklemezse, bu durumda göreli ithalat gerekmez ve bu sorunla karşılaşmazsınız).
Alex Dupuy

1
bir modül olarak çalıştırdığınızda core_test içeren dizinde olamayacağınızdan da bahsetmelisiniz (bu çok kolay olurdu)
Joseph Garvin

25

Core_test.py'de aşağıdakileri yapın:

import sys
sys.path.append('../components')
from core import GameLoopEvents

10

Kullanım durumunuz testleri çalıştırmak içinse ve bunun dikişi ise, aşağıdakileri yapabilirsiniz. Test komut dosyanızı çalıştırmak gibi python core_test.pybir test çerçevesi kullanın pytest. Sonra komut satırına girebilirsiniz

$$ py.test

Bu, dizininizdeki testleri çalıştıracaktır. Bu, @BrenBarn tarafından işaret edilen __name__varlık sorununu __main__çözer. Sonra, __init__.pytest dizininize boş bir dosya koyun , bu test dizinini paketinizin bir parçası haline getirecektir. Sonra yapabilirsin

from ..components.core import GameLoopEvents

Ancak, test komut dosyanızı ana program olarak çalıştırırsanız, işler bir kez daha başarısız olur. Bu yüzden sadece test çalıştırıcısını kullanın. Belki de bu gibi diğer test koşucular ile çalışır nosetestsama ben kontrol etmedim. Bu yardımcı olur umarım.


9

Benim hızlı düzeltme dizini yola eklemek için:

import sys
sys.path.insert(0, '../components/')

6
'../' bölümü komut dosyanızı çalıştırdığınız dizinden (core_test.py) çözüldüğü için yaklaşımınız her durumda çalışmaz. Yaklaşımınızla, core_test.py scritp'i çalıştırmadan önce 'testlere' cd yapmaya mecbur kalırsınız.
xyman

7

Sorun test yönteminizde,

denedin python core_test.py

bu hatayı alırsınız ValueError: Paket olmayan pakete göreli içe aktarma girişimi

Sebep: Ambalajınızı ambalaj dışı kaynaktan test ediyorsunuz.

modülünüzü paket kaynağından test edin.

bu proje yapınızsa,

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

cd pkg

python -m tests.core_test # dont use .py

veya pkg dışından /

python -m pkg.tests.core_test

.Aynı dizindeki klasörden içe aktarmak istiyorsanız single . geri her adım için bir tane daha ekleyin.

hi/
  hello.py
how.py

içinde how.py

from .hi import hello

nasıl merhaba.py'den içe aktarmak istiyorsanız

from .. import how

1
Kabul edilenden daha iyi cevap
GabrielBB

Örnekte from .. import how, belirli bir sınıfı / yöntemi 'how' dosyasından nasıl içe aktarıyorsunuz? eşdeğerini from ..how import fooyaptığımda "üst düzey paketin ötesinde göreceli içe aktarma girişimi" alıyorum
James Hulse

3

Eski iplik. Bir ekleme öğrendim __all__= ['submodule', ...]için __init__.py sonra dosya ve kullanan from <CURRENT_MODULE> import *hedef cezası çalışır.


3

Sen kullanabilirsiniz from pkg.components.core import GameLoopEventsörnek ben kullanım pycharm için, aşağıda benim proje yapısı görüntü, sadece o zaman çalışır, kök paketinden içe:

resim açıklamasını buraya girin


3
Bu benim için işe yaramadı. Yapılandırmanızdaki yolu ayarlamanız gerekti mi?
Mohammad Mahjoub

3

As Paolo söyledi biz 2 çağırma yöntemleri var:

1) python -m tests.core_test
2) python tests/core_test.py

Aralarındaki farklardan biri sys.path [0] dizesidir. Yana ithalat yaparken sys.path arayacaktır yorumlamak , biz yapabiliriz tests/core_test.py:

if __name__ == '__main__':
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
    from components import core
    <other stuff>

Ve bundan sonra, core_test.py dosyasını diğer yöntemlerle çalıştırabiliriz:

cd tests
python core_test.py
python -m core_test
...

Not, sadece py36 test edilmiştir.


3

Bu yaklaşım benim için çalıştı ve bazı çözümlerden daha az karmaşık:

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

Üst dizin benim PYTHONPATH __init__.pyiçinde ve üst dizinde ve bu dizinde dosyalar var .

Yukarıdakiler her zaman python 2'de çalıştı, ancak python 3 bazen bir ImportError veya ModuleNotFoundError'a çarptı (ikincisi python 3.6 ve ImportError alt sınıfında yenidir), bu nedenle aşağıdaki tweak hem python 2 hem de 3'te benim için çalışır:

try:
  from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
  from components.core import GameLoopEvents


1

Birisi geçici bir çözüm arıyorsa, bir sorunla karşılaştım. İşte biraz bağlam. Bir dosyada kullandığım yöntemlerden birini test etmek istedim. İçimden koştuğumda

if __name__ == "__main__":

her zaman göreceli ithalattan yakınıyordu. Yukarıdaki çözümleri uygulamaya çalıştım, ancak her biri birden çok içe aktarma içeren birçok iç içe dosya olduğundan işe yaramadı.

İşte yaptığım şey. Yeni bir başlatıcı, gerekli yöntemleri içeri aktaran ve onları çağıran harici bir program oluşturdum. Harika bir çözüm olmasa da işe yarıyor.


0

İşte herkesi kızdıracak ama oldukça iyi çalışacak bir yol. Testlerde:

ln -s ../components components

Ardından, normalde yaptığınız gibi bileşenleri içe aktarın.


0

Bu çok kafa karıştırıcı ve IDE'yi pycharm gibi kullanıyorsanız, biraz daha kafa karıştırıcı. Benim için ne işe yaradı: 1. pycharm proje ayarlarını yapın (VE'den veya python dizininden python çalıştırıyorsanız) 2. Tanımladığınız şekilde yanlış yoktur. bazen folder1.file1 import sınıfından çalışır

Eğer çalışmazsa, import folder1.file1 3 komutunu kullanın. Ortam değişkeniniz sistemde doğru şekilde belirtilmeli veya komut satırı argümanınızda belirtilmelidir.


-2

Kodunuz if __name__ == "__main__"bir paket olarak içe aktarılmadığından sys.path.append(), sorunu çözmek için daha iyi kullanırsınız .


if __name__ == "__main__"Dosyanıza sahip olmanın içe aktarma ile ilgili herhangi bir şeyde fark yarattığını düşünmüyorum .
user48956
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.