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


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.test를 from ..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디렉토리 A와 test_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-anchor 를 test_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.