개발기록장

[Python] 파이썬 모듈과 패키지(2) - 절대경로와 상대경로 + ImportError 본문

TIL/Python

[Python] 파이썬 모듈과 패키지(2) - 절대경로와 상대경로 + ImportError

yangahh 2021. 1. 16. 07:45

 

이전 글 읽기

2021/01/16 - [TIL/알고리즘 with 파이썬] - [Python] 파이썬 모듈과 패키지(1) - 개념

 

 

이전 글에서 파이썬 모듈과 패키지에 대해 알아보았다.

 

이번 글에서는 calculator라는 패키지를 직접 만들어보면서 import 시 알아야 할 절대 경로와 상대 경로에 대해 알아보자. 

 

 

 

1. Absolute path  vs  Relative path

 

- 절대 경로와 상대 경로 차이점

절대 경로는 import하는 - 파일이나 경로에 상관 없이 항상 프로젝트의 최상위 디렉토리에서 시작되는 경로를 말한다.

절대 경로를 사용하면 사용하고자 하는 모듈과 그 모듈의 패키지 구조를 알기 쉽다는 장점이 있다.

반면, 경로가 길어진다는 단점도 있다.

 

상대 경로는 import하는 위치를 기준으로 시작되는 경로를 말한다.

상대 경로로 나타낼 때 '.'은 import가 선언되는 파일의 현재 위치를 의미하고 '..'은 현재 위치의 상위 디렉토리를 의미한다.

상대 경로를 사용하면 절대 경로를 사용할 때에 비해 코드는 짧아질 수 있지만,

경로를 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점이 있다.

 

예시) 

# 절대 경로
from package2.subpackage1.module5 import func1 


# 상대 경로 
# package2/module3.py 에서 import
from .subpackage1.module5 import func2

# subpackage1/module5.py 에서 import
from ..module4 import class4

 

 

2. 상대 경로와 에러

 

우선 예제로 살펴 볼 my_project라는 프로젝트의 calculator 패키지 구조와 각 모듈의 내용은 아래와 같다.

 

# main.py

from .calculator.add_and_multiply import add_and_multiply


if __name__ == '__main__':
    print(add_and_multiply(1,2))
    
# add_and_multiply.py
 
from .multiplication import multiply

def add_and_multiply(a,b):
    return multiply(a,b) + (a+b)

# multiplication.py

def multiply(a,b):
    return(a*b)

 

main.py를 실행시키면 아래의 에러가 난다.

$ python3 main.py 

Traceback (most recent call last):

  File "main.py", line 6, in <module>

    from .calculator.add_and_multiply import add_and_multiply

ImportError: attempted relative import with no known parent package

부모 패키지도 모르는데(=스크립트로 직접 실행하는데) 감히 상대경로 import를 시도했다 라는 뜻이다.

python3 부터는 파이썬 자체에서 패키지와 모듈을 가져와 함수를 쓰는 곳에서는 상대경로로 접근자체가 불가능하게 막아놓았다.

즉 스크립트로 직접 실행할 때가 아닌 경우는 상대경로로 import할 수 없다는 얘기다. (파이썬 공식 문서 참고)

 

** 참고로 패키지 안에 있는 모듈을 스크립트로 실행하는 것은 안티패턴이라고 한다..

 

 

- 해결 방안 1

상대 경로로 import 된 것을 절대 경로로 바꿔서 실행해보자.

 

- 해결 방안 2

__name__ 변수를 이용하는 방법.

if __name__ == '__main__':  이 구문의 의미는 '만일 이 파일이 인터프리터에 의해서 실행되는 경우라면' 라는 의미이다.

따라서 '인터프리터에 의해서 실행되는 경우' 일 때만 상대경로로 import 되게 설정하는 방법도 있다.

>> 딱 봐도 좋아 보이진 않는다...여기서는 __name__ 변수를 어떻게 활용하는지만 보고, 이러니 저러니 그냥 절대경로를 쓰자..

 

 

참고 링크

blog.potados.com/dev/python3-import/

wikidocs.net/1418