TIL - Wecode | python-Import Search

한성봉·2021년 5월 17일
1

Import search

파이썬에서 모듈이나 패키지를 찾을 때 다음과 같은 순서로 찾습니다.

  1. sys.modules

    • 파이썬이 모듈이나 패키지를 찾을 때 가장 먼저 검색하는 곳
    • directory 형태
    • 이미 import 된 모듈이나 패키지를 저장하는 곳, 즉 한번 import 된 모듈이나 패키지를 파이썬이 다시 찾지 않아도 된다.
    • 새로 import하는 모듈은 sys.modules에서 찾을 수 없다.
  2. built-in modules

    • 파이썬에서 제공하는 공식 라이브러리
    • 내장 함수들이 저장된 라이브러리

    우리가 그냥 꺼내 쓰는 함수들은 이미 구현되있는 파이썬 라이브러리이다.

  3. sys.path

    • 마지막으로 검색하는 장소
    • string 요소들을 가지고 있는 list형태
    • 파이썬 list를 하나하나 확인하면서 원하는 모듈과 패키지를 확인

참고: sys는 파이썬이 가지고 있는 모듈이다. 그러므로 sys 모듈을 import해서 sys.modulessys.path 를 출력하거나 수정할 수 있다.

import sys

print(sys.modules)
print(sys.path)

결론적으로 파이썬은 위와 같은 순서로 모듈이나 패키지를 검색한다. 하지만 이 3개의 경로에서 찾지 못한다면 ModuleNotFoundError를 출력한다.


Absolute Path & Relative Path

Absolute Path

Absolute Path 는 가장 최상위 디렉토리부터 검색을 시작한다. 그렇기 때문에 현재 어느 디렉토리에서 검색을 하든 항상 경로가 동일하다.

참고로 current directory 라고 하는 현재의 프로젝트 디렉토리는 default로 sys.path 에 포함되게 됩니다.

일반적으로 local packageimport 할 때는 absolute path를 사용하면 된다. 하지만 absolute path를 사용할 때 단점은 항상 최상위 디렉토리부터 검색하기 때문에 경로가 길어질 수 있다는 것이다.

이러한 단점을 보완하기 위해 relative path를 사용한다.

여기서 local packageBuilt-in modulepip를 통해 설치한 외부 모듈이 아닌 직접 개발한 모듈의 패키지를 말한다.

Relative Path

absolute path와의 차이점은 import할 때 최상위 디렉토리부터 검색하는 것이 아니라 import하는 위치를 기준으로 경로를 설정한다.

일반적으로 local package안에서 다른 local packageimport할 때 사용된다.

# package2/module3.py

from . import class1
from .subpackage1.module5 import function2

여기서 닷 .import가 선언되는 현재 파일 위치를 나타낸다.
닷 2개.. 는 현재위치에서 상위 디렉토리로 가능 경로이다.

relative path는 경로의 길이를 줄여준다는 장점이 있지만, 헷갈리기 쉽고 파일 위치가 변경되면 경로도 바꿔야 되는 단점이 있다. 그러므로 웬만하면
absolute path를 사용하는 것이 권장된다.


예제

Path(경로)에 대한 예제를 한번 풀어보자.

  1. calculate 패키지 만들기

main.py

# absoulte path
# from calculator.add_and_multiply import add_and_multiply 

# relative path
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
# from calculator.multiplication import multiply

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

multiplication.py

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

2.main.py에서 상대경로로 add_and_mutiply 를 임포트 했을 때 발생하는 에러를 확인하고, 다음의 파이썬 공식 문서를 참고해서 main module 에서는 패키지의 모듈을 어떻게 임포트 해야하는지 블로깅 해주세요.

https://docs.python.org/3/tutorial/modules.html#intra-package-references

우선 위 문제대로 실행하게 되면 다음과 같은 오류창이 뜬다.

# ImportError: attempted relative import with no known parent package

import 에러고 부모 패키지를 알수 없다고 한다.
위의 공식문서를 참고해서 에러를 해결해보자.

Note that relative imports are based on the name of the current module. Since the name of the main module is always "main", modules intended for use as the main module of a Python application must always use absolute imports.

해석해보면 상대 import 는 현재 모듈의 이름을 기반으로 합니다. 주 모듈의 이름은 항상 "__main__" 이므로 python 애플리케이션의 주 모듈로 사용하려는 모듈은 항상 절대경로로 import 해야합니다.

그렇기 때문에 main.py 에서 다른 경로의 모듈,패키지,함수,클래스를 사용할려면 항상 절대 경로를 사용해야한다는 뜻입니다.

  1. add_and_multiply.py에서 multiply함수를 절대경로와 상대경로도 각각 임포트 해보고 main 모듈과 차이점을 생각해보고 결과를 출력해 보세요.
from .multiplication import multiply
# from calculator.multiplication import multiply


def add_and_multiply(a, b):
    return multiply(a, b) + (a+b)
# ImportError: attempted relative import with no known parent package

일단 add_and_multiply.py에서 상대경로로 multiply의 함수를 import하면
ImportError: attempted relative import with no known parent package 에러 메세지가 출력된다. main.py에서 봤던 에러랑 같은 에러가 출력된다.

마찬가지로 main moduleadd_and_multiply.py 의 위치를 잡지 못해 발생한 에러이다. 절대경로로 바꿔준다면 문제없이 실행이 된다.

같은 패키지 내에 모듈에서 다른 모듈의 기능을 사용하고 싶다면 사용하고 싶은 모듈의 이름을 바로 import 해주면 된다.

  1. ____init____.py 의 역할
  • ____init____.py 의 역할은 해당 디렉토리가 파이썬의 패키지라는 것을 알려준다. 패키지를 초기화하는 역할도 한다. python3.3 이상부터는 ____init____.py이 없더라도 패키지로 인식되지만 하위버전과 상위버전의 호환을 위해 ____init____.py파일을 만들어 주는 것을 권장한다. ____init____.py 파일 안에는 내용을 기본적으로 비워두면 된다.

  • all
    from <모듈 이름> import *
    * 은 해당 모듈의 모든 기능을 사용하겠다는 의미이다. 하지만 from + <모듈이름>을 적어줄 떄 from 모듈이름.모듈이름.모듈이름 의 양식으로 작성한다.
    하지만 위의 양식에서 루트 디렉토리-> 서브 디렉토리 -> 모듈의 순으로 들어가게 되는데
    from 루트디렉토리 -> 서브디렉토리 import * 하게 되면 해당 기능을 사용할 수 없다. 서브디렉토리안의 모듈이 있더라도 어느 모듈의 기능을 사용하고 싶은지 인식하지 못하기 때문에 기능을 사용하고 싶은 서브디렉토리의 ____init____.py 파일에서 기능을 사용하고 싶은 모듈을 all로 정의 해줘야한다.

__all__ = ['모듈 이름']

이것의 ____init____.py 의 두 번째 기능이다.

상대 경로 문제 피해가기는 다음의 링크를 참조해보자

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

0개의 댓글