__init__.py를 사용하여“비 패키지에서 시도 된 상대 가져 오기”를 수정하는 방법


744

다음 디렉토리 구조로 PEP 328 을 따르려고합니다 .

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

에서 core_test.py나는 다음과 같은 import 문을

from ..components.core import GameLoopEvents

그러나 실행할 때 다음 오류가 발생합니다.

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

주위를 검색하면 " __init__.py " 와도 " 상대 경로가 작동하지 않습니다 "와 " 상대 경로에서 모듈 가져 오기 "가 발견되었지만 도움이되지 않았습니다.

여기서 누락 된 것이 있습니까?


17
또한 다양한 구조의 unittest프로젝트 구성 방법에 대해 매우 혼란스러워서 모듈의 중첩, 상대 및 절대 가져 오기 (작업이 있고 그렇지 않은 경우) 및 상대 및 절대 참조를 다루는 상당히 철저한 샘플 프로젝트 를 작성했습니다 . 패키지, 단일, 이중 및 패키지 수준의 클래스 가져 오기. 나를 위해 바로 물건을 정리하는 데 도움이 되었습니다!
cod3monk3y

1
나는 당신의 시험을 작동시킬 수 없었습니다. no module named myimports.foo내가 그들을 실행할 때 계속 받고 있습니다.
Blairg23 23.

@ Blairg23 예를 들어 의도 된 호출이에 실행 cd되고 있다고 추측합니다 . PyImportspython -m unittest tests.test_abs
duozmo

7
나는 유전자에 동의합니다. 좀 더 도움이되는 가져 오기 프로세스를 디버깅하는 메커니즘이 있었으면 좋겠다. 제 경우에는 같은 디렉토리에 두 개의 파일이 있습니다. 한 파일을 다른 파일로 가져 오려고합니다. 해당 디렉토리에 init .py 파일 이 있으면 ValueError : Non-package 오류로 상대 가져 오기를 시도했습니다. init .py 파일을 제거하면 'NAME'이라는 이름의 모듈 없음 오류가 표시됩니다.
user1928764

제 경우에는 같은 디렉토리에 두 개의 파일이 있습니다. 한 파일을 다른 파일로 가져 오려고합니다. 해당 디렉토리에 init .py 파일 이 있으면 ValueError : Non-package 오류로 상대 가져 오기를 시도했습니다. init .py 파일을 제거하면 'NAME'이라는 이름의 모듈 없음 오류가 표시됩니다. 정말 실망스러운 점은이 작업을 수행 한 다음 PYTHONPATH를 무언가로 설정 한 .bashrc 파일을 삭제하여 발자국을 쏴서 작동하지 않는다는 것입니다.
user1928764

답변:


443

예. 패키지로 사용하지 않습니다.

python -m pkg.tests.core_test

51
문제 : 끝에 '.py'가 없습니다.
mindthief

497
나는 downvoters 중 하나가 아니지만 이 질문과 답변의 인기를 감안할 때 이것이 상당히 세부적으로 사용할 수 있다고 생각합니다 . 위의 쉘 명령을 실행할 디렉토리와 같은 것을 지적 __init__.py하고, 항상 아래로 내려야 한다는 사실 과 __package__아래의 BrenBarn에 의해 설명되는 -modifying 속임수 (예 : shebang 및 ./my_script.py유닉스 쉘에서 작업하는 것이 모두 유용 할 것입니다. 이 전체 문제는 간결하고 이해하기 쉬운 문서를 찾거나 찾는 것이 까다로 웠습니다.
Mark Amery

16
참고 : pkgCLI에서이 줄을 호출 할 때 디렉토리 외부에 있어야합니다 . 그런 다음 예상대로 작동합니다. 안에 pkg있고 전화하면 python -m tests.core_test작동하지 않습니다. 적어도 그것은 나를 위해하지 않았다.
Blairg23

94
진지하게, 당신은 당신의 대답에 무슨 일이 일어나고 있는지 설명 할 수 있습니까?
피노키오

18
@MarkAmery 파일이있는 py 파일이있는 하위 디렉토리가있는 프로젝트 내에서 상대적 가져 오기를 수행하려고 시도 __init__.py하면서 ValueError: Attempted relative import in non-package오류가 계속 발생 한다는 사실을 거의 잃어 버렸습니다 . 나는 어딘가에 누군가에게 정말 좋은 돈을 지불하고 마침내 모든 것이 어떻게 작동하는지 일반 영어로 설명 할 것입니다.
AdjunctProfessorFalcon

635

Ignacio Vazquez-Abrams의 답변 을 자세히 설명하려면 다음과 같이하십시오.

파이썬 가져 오기 메커니즘은 __name__현재 파일과 관련하여 작동 합니다. 파일을 직접 실행할 때는 일반적인 이름이 없지만 "__main__"대신 이름이 있습니다. 따라서 상대 수입품이 작동하지 않습니다.

Igancio가 제안한대로 -m옵션을 사용하여 실행할 수 있습니다. 패키지로 스크립트로 실행되도록 패키지의 일부가있는 경우 해당 __package__속성을 사용하여 해당 파일에 패키지 계층 구조에있는 이름 을 지정할 수도 있습니다 .

자세한 내용은 http://www.python.org/dev/peps/pep-0366/ 을 참조하십시오.


55
하위 디렉토리 python -m core_test내에서 실행할 수 없다는 것을 알기 위해 시간이 걸렸습니다 tests. 부모 디렉토리에서 시작해야하거나 부모를 경로에 추가해야합니다.
Aram Kocharyan

3
@DannyStaple : 정확하지 않습니다. __package__실행 가능한 스크립트 파일이 동일한 패키지 내에서 다른 모듈을 상대적으로 가져올 수 있도록 하는 데 사용할 수 있습니다 . "전체 시스템"에서 상대적으로 가져올 방법이 없습니다. 왜 당신이 이것을하고 싶은지 잘 모르겠습니다.
BrenBarn

2
경우 내 말은 __package__기호가 "parent.child"다음 "parent.other_child"를 가져올 수있을 것으로 설정됩니다. 어쩌면 나는 그렇게 잘 표현하지 못했습니다.
대니 스테이플

5
@DannyStaple : 작동 방식은 링크 된 문서에 설명되어 있습니다. 당신은 스크립트가있는 경우 script.py패키지를 pack.subpack, 그 때의 설정 __package__pack.subpack당신이 할 수있게된다 from ..module import something로부터 수입 뭔가 pack.module. 설명서에 나와 있듯이 여전히 시스템 경로에 최상위 패키지가 있어야합니다. 이것은 이미 가져온 모듈에서 작동하는 방식입니다. 유일한 방법 __package__은 직접 실행 된 스크립트에도 해당 동작을 사용할 수 있다는 것입니다.
BrenBarn

3
__package__직접 실행되는 스크립트에서 사용 하지만 불행히도 다음과 같은 오류가 발생합니다. "부모 모듈 'xxx'이 (가)로드되지 않았습니다. 상대적 가져 오기를 수행 할 수 없습니다"
mononoke

202

import components.core현재 디렉토리를 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__), '..')))이것은 또한 작동 할 것이다
ajay

26
from os import sys부정 행위처럼 보인다 :)
날으는 양

3
@Piotr : sys.path현재 파일이있는 디렉토리의 상위에 추가되는 내용을 약간 더 명확하게 보여주기 때문에 더 나은 것으로 간주 될 수 있습니다 .
martineau

8
@flyingsheep : 동의합니다. 나는 단지 regular을 사용합니다 import sys, os.path as path.
martineau

10
참고로, ipython 노트북에서 이것을 사용하기 위해이 답변을 다음에 적용했습니다 import os; os.sys.path.append(os.path.dirname(os.path.abspath('.'))). 그런 다음 import components.core원하는대로 노트북의 상위 디렉토리에서 가져 오는 직선이 작동합니다.
올챙이 경주

195

스크립트를 시작하려는 방법에 따라 다릅니다.

고전적인 방법으로 명령 행 에서 UnitTest시작하려면 다음과 같습니다.

python tests/core_test.py

이 경우 'components''tests' 는 형제 폴더이므로 sys.path 모듈 의 insert 또는 append 메소드를 사용하여 상대 모듈을 가져올 수 있습니다 . 다음과 같은 것 :

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

그렇지 않으면 '-m'인수를 사용하여 스크립트를 시작할 수 있습니다 (이 경우 패키지에 대해 이야기하고 있으므로 '.py' 확장자 를 지정해서는 안 됨 ).

python -m pkg.tests.core_test

이 경우 상대 임포트를 간단하게 사용할 수 있습니다.

from ..components.core import GameLoopEvents

마지막으로 두 가지 접근 방식을 혼합하여 스크립트 호출 방식에 관계없이 스크립트가 작동하도록 할 수 있습니다. 예를 들면 다음과 같습니다.

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
디버깅에 pdb를 사용하려는 경우 어떻게해야합니까? python -m pdb myscript.py디버깅 세션을 시작하는 데 사용 하기 때문 입니다.
danny

1
@dannynjust-2 개의 메인 모듈을 가질 수 없기 때문에 좋은 질문입니다. 일반적으로 디버깅 할 때 디버깅을 시작하려는 첫 번째 지점에서 수동으로 디버거를 수동으로 사용하는 것을 선호합니다. import pdb; pdb.set_trace()코드에 인라인 을 삽입하면 됩니다.
mgilson

3
insert대신 사용 하는 것이 더 낫 append습니까? 즉,sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
SparkAndShine

2
삽입을 사용하면 로컬 패키지 이름이 설치된 패키지보다 우선하는 상대적 가져 오기 시맨틱에 더 적합합니다. 특히 테스트의 경우 일반적으로 설치된 버전이 아닌 로컬 버전을 테스트하려고합니다 (테스트 인프라에서 테스트중인 코드를 설치하지 않는 경우 상대적 가져 오기가 필요하지 않으며이 문제가 발생하지 않음).
Alex Dupuy

1
또한 모듈로 실행할 때 core_test를 포함하는 디렉토리에있을 수 없습니다 (너무 쉬울 것입니다)
Joseph Garvin

25

core_test.py에서 다음을 수행하십시오.

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

10

유스 케이스가 테스트를 실행하기위한 것이고 그것이 솔기 인 경우 다음을 수행 할 수 있습니다. 테스트 스크립트를 실행 python core_test.py하는 대신와 같은 테스트 프레임 워크 를 사용하십시오 pytest. 그런 다음 명령 행에 다음을 입력 할 수 있습니다.

$$ py.test

그러면 디렉토리에서 테스트가 실행됩니다. 이 문제 걸어 다니는 __name__존재 __main__@BrenBarn 지적했다. 다음으로, __init__.py테스트 디렉토리에 빈 파일을 넣으면 테스트 디렉토리가 패키지의 일부가됩니다. 그럼 당신은 할 수있을 것입니다

from ..components.core import GameLoopEvents

그러나 테스트 스크립트를 기본 프로그램으로 실행하면 다시 실패합니다. 테스트 러너를 사용하십시오. 어쩌면 이것은 다른 테스트 러너와도 작동 nosetests하지만 확인하지 않았습니다. 도움이 되었기를 바랍니다.


9

내 빠른 해결책은 디렉토리를 경로에 추가하는 것입니다.

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

6
'../'부분이 스크립트를 실행하는 디렉토리 (core_test.py)에서 분석되므로 접근 방식이 모든 경우에 작동하지 않습니다. 접근 방식을 사용하면 core_test.py scritp를 실행하기 전에 'tests'로 cd해야합니다.
xyman

7

테스트 방법에 문제가 있습니다.

당신은 시도 python core_test.py

그러면이 오류가 발생합니다 합니다.

이유 : 비 패키지 소스에서 패키징을 테스트하고 있습니다.

패키지 소스에서 모듈을 테스트하십시오.

이것이 프로젝트 구조 인 경우

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

cd pkg

python -m tests.core_test # dont use .py

또는 외부 pkg /

python -m pkg.tests.core_test

.동일한 디렉토리의 폴더에서 가져 오려면 single . 각 단계마다 다시 하나 더 추가하십시오.

hi/
  hello.py
how.py

how.py

from .hi import hello

hello.py에서 방법을 가져 오려면 incase

from .. import how

1
답변보다 낫습니다
GabrielBB

이 예제에서는 from .. import how'how'파일에서 특정 클래스 / 메소드를 가져 오는 방법을 설명합니다. 그와 동등한 작업을 수행하면 from ..how import foo"최상위 패키지 이상의 상대적인 수입을 시도했습니다"
James Hulse

3

오래된 실. 나는를 추가하는 것을 알아 냈다 __all__= ['submodule', ...]받는 __init__.py의 다음 파일과 사용 from <CURRENT_MODULE> import *대상에서 잘 작동합니다.


3

from pkg.components.core import GameLoopEvents예를 들어 pycharm을 사용할 수 있습니다 . 아래는 내 프로젝트 구조 이미지입니다. 루트 패키지에서 가져 오면 작동합니다.

여기에 이미지 설명을 입력하십시오


3
이것은 나를 위해 작동하지 않았습니다. 구성에서 경로를 설정해야 했습니까?
Mohammad Mahjoub

3

파올로가 말했다, 우리는이 개 호출 방법이있다 :

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

그들 사이의 한 가지 차이점은 sys.path [0] 문자열입니다. 해석은 import 할 때 sys.path를 검색 하므로 다음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>

그 이후에도 다른 방법으로 core_test.py를 실행할 수 있습니다.

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

py36은 테스트를 마쳤습니다.


3

이 방법은 저에게 효과적이며 일부 솔루션보다 덜 복잡합니다.

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

부모 디렉토리는 내 PYTHONPATH __init__.py에 있으며 부모 디렉토리와이 디렉토리에 파일이 있습니다.

위의 내용은 항상 python 2에서 작동했지만 python 3은 때때로 ImportError 또는 ModuleNotFoundError (후자는 python 3.6의 새로운 클래스 및 ImportError의 하위 클래스)에 도달하므로 python 2 및 3 모두에서 다음과 같은 비틀기가 작동합니다.

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


1

누군가가 해결책을 찾고 있다면 나는 그것을 우연히 발견했습니다. 약간의 맥락이 있습니다. 파일에서 사용한 방법 중 하나를 테스트하고 싶었습니다. 내가 안에서 그것을 실행할 때

if __name__ == "__main__":

그것은 항상 상대적 수입에 대해 불평했다. 위의 솔루션을 적용하려고했지만 여러 개의 가져 오기가있는 중첩 파일이 많았 기 때문에 작동하지 않았습니다.

여기 내가 한 일이 있습니다. 방금 필요한 메소드를 가져 와서 호출하는 외부 프로그램 인 런처를 만들었습니다. 훌륭한 해결책은 아니지만 작동합니다.


0

모든 사람을 화나게하지만 꽤 잘 작동하는 한 가지 방법이 있습니다. 테스트 실행에서 :

ln -s ../components components

그런 다음 평소처럼 구성 요소를 가져옵니다.


0

이것은 매우 혼란스럽고 pycharm과 같은 IDE를 사용하는 경우 조금 더 혼란 스럽습니다. 나를 위해 일한 것 : 1. pycharm 프로젝트 설정을 지정하십시오 (VE 또는 Python 디렉토리에서 python을 실행하는 경우) 2. 정의한 방법이 잘못되었습니다. 때로는 folder1.file1 가져 오기 클래스에서 작동합니다.

작동하지 않으면 import folder1.file1을 사용하십시오. 3. 환경 변수가 시스템에서 올바르게 언급되거나 명령 행 인수에 제공되어야합니다.


-2

코드 if __name__ == "__main__"에 패키지로 가져올 수없는가 포함되어 sys.path.append()있으므로 문제를 해결하는 데 더 좋습니다 .


if __name__ == "__main__"파일에 포함시키는 것이 가져 오기와 관련이 있다고 생각하지 않습니다 .
user48956
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.