상대적 가져 오기에서 최상위 패키지 오류를 넘어서


316

파이썬 3의 상대적 가져 오기에 대해서는 이미 꽤 많은 질문이 있지만 많은 것들을 거친 후에도 여전히 내 문제에 대한 답을 찾지 못했습니다. 여기 질문이 있습니다.

아래에 표시된 패키지가 있습니다

package/
   __init__.py
   A/
      __init__.py
      foo.py
   test_A/
      __init__.py
      test.py

test.py에 한 줄이 있습니다.

from ..A import foo

지금은의 폴더에 package있으며

python -m test_A.test

나는 메시지를 받았다

"ValueError: attempted relative import beyond top-level package"

그러나 내가 package예를 들어 의 상위 폴더에 있다면 다음을 실행합니다.

cd ..
python -m package.test_A.test

다 괜찮아

이제 내 질문은 : 의 폴더에있을 때 packagetest_A 하위 패키지 내부에서 모듈을 실행합니다 test_A.test. 내 이해에 ..A따라 여전히 package폴더 내에있는 한 수준 만 올라갑니다 beyond top-level package. 왜 메시지가 나타납니다 . 이 오류 메시지를 발생시키는 이유는 무엇입니까?


49
그 게시물은 "최상위 패키지를 넘어서"내 오류를 설명하지 못했습니다
shelper

4
여기에서 생각합니다. test_A.test를 모듈로 실행할 때 '..'은 이미 test_A.test 가져 오기의 최상위 레벨 인 test_A를 넘어서지 만 패키지 레벨은 디렉토리 레벨이 아니라고 생각합니다. 패키지를 가져 오는 레벨.
shelper

2
이 답변 stackoverflow.com/a/14132912/8682868을 본 후 상대 수입에 대한 모든 것을 이해할 것이라고 약속드립니다 .
pzjzeason

이 문제에 대한 자세한 설명 은 ValueError : 최상위 패키지 이외의 상대 가져 오기 시도를 참조하십시오 .
napuzba

상대 수입을 피하는 방법이 있습니까? Eclipse의 PyDev가 <PydevProject> / src 내의 모든 패키지를 보는 방식과 같은?
Mushu909

답변:


172

편집 : 다른 질문 에이 질문에 대한 더 좋고 더 일관된 답변이 있습니다.


왜 작동하지 않습니까? 파이썬이 패키지가로드 된 위치를 기록하지 않기 때문입니다. 따라서 할 때 python -m test_A.test기본적으로 test_A.test실제로 저장된 지식을 버립니다 package(즉 package, 패키지로 간주되지 않음). 시도는 from ..A import foo그것이 더 이상 (로드 된 위치, 즉 형제 디렉토리)이없는 액세스 정보에 노력하고있다. from ..os import path의 파일 을 허용 하는 것과 개념적으로 비슷합니다 math. 패키지를 구별하기를 원하기 때문에 이것은 나쁠 것입니다. 다른 패키지의 무언가를 사용해야하는 경우 전 세계적으로 패키지를 참조 from os import path하고 파이썬이 $PATHand 와 함께있는 곳에서 작동하도록 해야합니다 $PYTHONPATH.

을 사용하면 내용을 추적 하고로드 된 위치의 하위 디렉토리에 액세스 하기 때문에 문제 python -m package.test_A.testfrom ..A import foo해결하는 것이 package좋습니다.

파이썬이 현재 작업 디렉토리를 패키지로 간주하지 않는 이유는 무엇입니까? CLUE 는 없지만 유용합니다.


2
나는 같은 것에 해당하는 질문에 대한 더 나은 답변을 참조하도록 내 답변을 편집했습니다. 해결 방법 만 있습니다. 내가 실제로 일한 것을 본 유일한 것은 OP가 수행 -m한 것입니다. 이 플래그를 사용하고 위의 디렉토리에서 실행됩니다.
Multihunter

1
주목해야한다 이 대답 Multihunter에 의해 주어진 링크에서은,은 포함되지 않습니다 sys.path해킹,하지만 사용 setuptools에 훨씬 더 흥미로운 내 의견이다.
Angelo Cardellicchio

157
import sys
sys.path.append("..") # Adds higher directory to python modules path.

이 시도. 나를 위해 일했다.


10
음 ... 어떻게 작동합니까? 모든 단일 테스트 파일에는 이것이 있습니까?
George Mauer

여기서 문제는 예를 들어 A/bar.py존재하고 foo.py당신 안에 있는 것 from .bar import X입니다.
user1834164

9
sys.path.append ( "..")를 추가 한 후 "from ..A import ..."에서 ..를 제거해야했습니다.
Jake OPJ

2
스크립트가 존재하는 디렉토리 외부에서 스크립트를 실행하면 작동하지 않습니다. 대신,이 스크립트의 절대 경로지정 하려면이 답변을 조정해야합니다 .
Manavalan Gajapathy 2018 년

이것은 가장 복잡하지 않은 최상의 옵션입니다
Alex R

43

가정 :
당신이있는 경우 package디렉토리 Atest_A별도의 패키지입니다.

결론 :
..A가져 오기는 패키지 내에서만 허용됩니다.

추가 참고 사항 :
패키지 내에서만 사용할 수있는 상대 가져 오기를 만드는 것은 패키지를에있는 경로에 강제로 배치하려는 경우에 유용합니다 sys.path.

편집하다:

나는 이것이 미쳤다고 생각하는 유일한 사람입니까!? 왜 현재 작업 디렉토리가 패키지로 간주되지 않습니까? – 멀티 헌터

현재 작업 디렉토리는 일반적으로 sys.path에 있습니다. 따라서 모든 파일을 가져올 수 있습니다. 이것은 패키지가 아직 존재하지 않은 Python 2 이후의 동작입니다. 실행중인 디렉토리를 패키지로 만들면 모듈을 "import .A"로 가져오고 "import A"로 가져 오면 두 개의 다른 모듈이됩니다. 어쩌면 이것은 일관성이없는 것일 수도 있습니다.


85
나는 이것이 미쳤다고 생각하는 유일한 사람입니까!? 왜 세계에서 실행중인 디렉토리가 패키지로 간주되지 않습니까?
Multihunter

13
그 미친 것뿐만 아니라 도움이되지 않습니다 ... 그렇다면 어떻게 테스트를 실행합니까? 분명히 OP가 요구 한 것과 왜 많은 사람들이 여기에 있는지 확신합니다.
George Mauer

실행중인 디렉토리는 일반적으로 sys.path에 있습니다. 따라서 모든 파일을 가져올 수 있습니다. 이것은 패키지가 아직 존재하지 않은 Python 2 이후의 동작입니다. -편집 답변.
사용자

나는 불일치를 따르지 않습니다. 의 행동 python -m package.test_A.test은 원하는 것을 하는 것처럼 보이고, 나의 주장은 그것이 디폴트가되어야한다는 것입니다. 이 불일치의 예를 들어 주시겠습니까?
멀티 헌터

실제로 생각하고 있습니다. 기능 요청이 있습니까? 이것은 참으로 미쳤다. C / C ++ 스타일 #include은 매우 유용합니다!
Nicholas Humphrey

29

3.6에서 이러한 솔루션 중 어느 것도 다음과 같은 폴더 구조로 작동하지 않았습니다.

package1/
    subpackage1/
        module1.py
package2/
    subpackage2/
        module2.py

내 목표는 module1에서 module2로 가져 오는 것이 었습니다. 마침내 나를 위해 일한 것은 이상하게도 충분했습니다.

import sys
sys.path.append(".")

지금까지 언급 한 2 점 솔루션과 달리 단일 점에 유의하십시오.


편집 : 다음은 나를 위해 이것을 명확히하는 데 도움이되었습니다.

import os
print (os.getcwd())

필자의 경우 작업 디렉토리는 (예기치 않게) 프로젝트의 루트였습니다.


2
로컬에서 작동하지만 aws ec2 인스턴스에서는 작동하지 않습니다.
thebeancounter

이것은 저에게도 효과적이었습니다. 제 경우에는 작업 디렉토리가 프로젝트 루트였습니다. 나는 프로그래밍 편집기 (TextMate를)에서 실행 바로 가기를 사용했다
JeremyDouglass

@thebeancounter 같은! 내 Mac에서는 로컬로 작동하지만 ec2에서는 작동하지 않으며 ec2의 하위 디렉토리에서 명령을 실행하고 루트에서 로컬로 실행한다는 것을 알았습니다. ec2의 루트에서 실행하면 작동했습니다.
Logan Yang

이것은 또한 저에게 매우 감사했습니다. sys 메소드에서 ".."없이 간단하게 패키지를 호출 할 수 있습니다.
RamWill

sys.path.append(".")부모 디렉토리에서 호출했기 때문에 작동했습니다. .항상 python 명령을 실행하는 디렉토리를 나타냅니다.
KevinZhou

13

from package.A import foo

나는 그것이 더 분명하다고 생각

import sys
sys.path.append("..")

4
확실히 읽을 수는 있지만 여전히 필요합니다 sys.path.append(".."). python 3.6에서 테스트
MFA

이전 답변과 동일
nrofis

12

당신은 기본적으로 그 때문에 가장 인기있는 대답에서 알 수 있듯이 PYTHONPATH또는 sys.path포함 .하지만 경로 패키지에. 그리고 상대적 가져 오기는 가져 오기가 발생하는 파일이 아니라 현재 작업 디렉토리에 상대적입니다. 이상하게도

먼저 상대 가져 오기를 절대로 변경 한 다음 시작하여이 문제를 해결할 수 있습니다.

PYTHONPATH=/path/to/package python -m test_A.test

또는 이런 식으로 호출 될 때 파이썬 경로를 강제로 :

으로 python -m test_A.test당신이 실행하고 test_A/test.py함께 __name__ == '__main__'하고__file__ == '/absolute/path/to/test_A/test.py'

즉 , 주요 사례 조건에서 test.py절대 import반 보호를 사용하고 일회성 Python 경로 조작을 수행 할 수 있음을 의미합니다 .

from os import path

def main():

if __name__ == '__main__':
    import sys
    sys.path.append(path.join(path.dirname(__file__), '..'))
    from A import foo

    exit(main())

8

편집 : 2020-05-08 : 내가 인용 한 웹 사이트가 더 이상 조언을 작성한 사람이 제어하지 않는 것 같습니다. 그래서 사이트 링크를 제거하고 있습니다. baxx를 알려 주셔서 감사합니다.


이미 큰 답변을 제공 한 후에도 누군가가 여전히 어려움을 겪고 있다면 웹 사이트에서 더 이상 사용할 수 없다는 조언을 얻었습니다.

내가 언급 한 사이트의 필수 인용문 :

"이 방법으로 프로그래밍 방식으로 동일하게 지정할 수 있습니다.

수입 시스템

sys.path.append ( '..')

물론 위의 코드는 다른 import 전에 작성해야합니다 .

그것이 사실 후에 생각하고, 이런 식이어야한다는 것이 분명합니다. 내 테스트에서 sys.path.append ( '..')를 사용하려고했지만 OP가 게시 한 문제가 발생했습니다. 다른 가져 오기 전에 가져 오기 및 sys.path 정의를 추가하여 문제를 해결할 수있었습니다.


게시 한 링크가 죽었습니다.
baxx

알려 줘서 고마워. 도메인 이름이 더 이상 같은 사람에 의해 제어되지 않는 것 같습니다. 링크를 제거했습니다.
Mierpo

5

__init__.py상위 폴더에있는 경우 import file/path as alias해당 init 파일에서 와 같이 가져 오기를 초기화 할 수 있습니다 . 그런 다음 하위 스크립트에서 다음과 같이 사용할 수 있습니다.

import alias

0

나는 겸손한 의견으로이 질문을 다음과 같이 이해합니다.

[사례 1] 다음과 같이 절대 가져 오기를 시작할 때

python -m test_A.test

또는

import test_A.test

또는

from test_A import test

실제로 import-anchortest_A, 즉 최상위 패키지는로 설정 test_A합니다. 따라서 test.py do from ..A import xxx가 있으면 앵커에서 벗어나고 파이썬은 이것을 허용하지 않습니다.

[사례 2] 할 때

python -m package.test_A.test

또는

from package.test_A import test

앵커가된다 package, 그래서 package/test_A/test.py일을 from ..A import xxx(여전히 내부의 앵커를 탈출하지 않는 package폴더), 파이썬 행복이 받아들입니다.

한마디로 :

  • 절대 가져 오기는 현재 앵커를 변경합니다 (= 최상위 패키지가 무엇인지 재정의 함).
  • 상대 가져 오기는 앵커를 변경하지 않지만 이에 한정됩니다.

또한 FQMN ( 정규화 된 모듈 이름 )을 사용하여이 문제를 검사 할 수 있습니다.

각 경우에 FQMN을 확인하십시오.

  • [사례 2] test.__name__=package.test_A.test
  • [사례 1] test.__name__=test_A.test

따라서 CASE2의 경우 from .. import xxxFQMN = package.xxx인 새 모듈이 생성 됩니다.

CASE1의 경우 ..from 내 from .. import xxx에서의 시작 노드 (앵커)에서 뛰어 내리므로 test_APython에서는 허용되지 않습니다.


2
이것은 필요한 것보다 훨씬 복잡합니다. 파이썬의 젠에게 너무 많은.
AtilioA

0

python 2.x에서는 확실하지 않지만 python 3.6에서는 전체 제품군을 실행하려고한다고 가정하면 사용하면됩니다. -t

-t, --top-level-directory directory 프로젝트의 최상위 디렉토리 (디폴트 시작 디렉토리)

그래서, 같은 구조에서

project_root
  |
  |----- my_module
  |          \
  |           \_____ my_class.py
  |
  \ tests
      \___ test_my_func.py

예를 들어 다음을 사용할 수 있습니다.

python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/

그리고 여전히 my_module.my_class주요 드라마없이 가져옵니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.