We.TIL 06 : How Import Statement Finds Modules & Package

김기욱·2020년 7월 26일
0

We.TIL

목록 보기
10/69

서두

사람은 동물이다, 동물은 생물이며, 생물은 물질이다.
이를 괄호로 표현하면 이렇게 된다.

사람 < 동물 < 생물 < 물질

파이썬에선 모듈이라는 편리한 기능이 존재한다.
코드가 너무 길어지는 것을 방지하기 위해 짜여놓은 클래스, 함수, 변수등을
다른 파이썬 파일에서 실행할 수 있는 기능이다.

그리고 이 모듈 위에 상위 디렉토리, 즉 모듈을 모아놓은 폴더가 '패키지'다.
그러므로 Total Package에 있는 SuperMath Module에 x_multiply func(함수)를 쓰기 위해선 상위 디렉토리부터 차근차근 파고 들어가야 된다.

Total Package => SuperMath => x_multiply func

만일 당신이 이러한 모듈과 패키지를 파이썬파일에 쓰기 위해선 정확한 위치를 알고 해당위치를 import하는 것이 매우 중요하다. 만일 정확한 위치를 표기하지 않고 import를 한다면 모듈과 패키지는 정확하게 동작하지 않게 될 것이다. 그렇다면 파이썬은 이런 모듈/Package를 어떻게 찾는지, 즉 경로탐색에 관한 방식에 대한 이해가 필요할 것이다.

파이썬은 다음 3가지 장소를 순서대로 보면서 찾는다.

  1. sys.modules
  2. built-in modules
  3. sys.path

sys.modules는 단순한 dictionary다. 그리고 이미 import된 모듈과 package를 저장하고 있다. 한번 import된 모듈과 패키지들을 파이썬이 다시 찾지 않아도 되도록 하는 기능을 가지고 있다. sys.module은 사용된 module의 저장소같은 개념이다. 그러므로 사용자가 만약 새로 module을 import한다면 sys.modules에서 경로를 찾을 수 가 없다.

built-in modules는 파이썬에 기본적으로 default로 주어져있는 modules들이다. 이미 파이썬에 포함된 모듈들이기 때문에 파이썬이 쉽게 찾을 수 있다.

sys.path는 마지막으로 보는 장소다. sys.path는 기본적으로 리스트이며 string요소들을 가지고 있는 list다. 간단한 명령어로 sys.path의 형태를
확인해 볼 수 있다.

import sys
print(sys.path)

['/Users/choi/Desktop/Python', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python38.zip', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages']

Built-in modules의 경우 내장되어있어 찾기가 쉽고, pip로 설치한 외부모듈도 자동으로 site-package라는 디렉토리에 설치가 되는데, 이 site-package는 sys.path에 이미 포함되어있으므로 찾는데 문제가 발생하지 않는다.
문제는 직접 개발한 패키지인 local package다. 직접 개발한 local package를 import하기 위해선 해당 package 위치에 맞게 import 경로설정을 해야한다.
이때 쓰이는 것이 절대경로(abosolute)와 상대경로(relative)다.

다음과 같은 예시를 보자.
Absolute path를 통해 package1과 package2를 import하려면 다음과 같이
작성해야 한다.

from package1 import module1
from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2

경로들의 시작점이 전부 'my_app' 프로젝트의 가장 최상위 디렉토리에서 시작하는 것을 볼 수 있다. 예를 들어 subpackage1의 module5의 function2함수를 import하기 위해서는 다음 경로를 거치게 된다.

my_app => package2 => subpackage1 => module5.py
linux :: my_app/package2/subpackage1/module5.py

이미 my_app 프로젝트 안에 있으므로 my_app은 생락된다.
그러므로 다음과 같이 경로를 표현하게 된다.

from package2.subpackage1.module5 import function2

my_app 프로젝트 내에서는 어느 파일, 어느 위치에서 import하던지 경로는 항상 동일하게 되며 이를 absolute path라고 명명한다

같은 주소를 relativa path로 표현하면 이렇게된다.
(현재 위치는 package2의 module3으로 가정한다.)

from .subpackage1.module5 import function2

현재 위치는 package2/module3.py이므로 현재 위치에서부터 원하는 모듈의 경로만 선언해주면 된다. 여기서 dot(.)은 현재 위치를 의미한다.
dot(.)을 두 번 쓰게되면 현재 위치에서 상위 디렉토리로 가는 경로다.

#현재위치가 subpackage1/module5.py 인 경우
from ..module4 import class4

Assignment

  1. sys.modules와 sys.path의 차이점을 서술하시오

1 자료구조가 다르다(modules는 딕셔너리, path는 리스트)
2) sys.modules는 기존에 import한 package&modules를 저장한다.
3) 대신 sys.modules경로로 신규 패키지를 import할 수 없다.
4) sys.path는 신규로 생성한 local package를 불러올때 쓰여진다.

  1. 파이썬은 sys모듈의 위치를 어떻게 찾을 수 있을까요?

sys모듈은 내장되어(inherently)있는 built-in module이다. 그러므로 파이썬이 두 번째 검색 경로인 built-in module안에 sys경로를 찾아서 import를 할 수 있게된다.

참고용 : "sys"란?
시스템 특정 파라미터, 함수 (System - specific parameters and funcions) 인터프리터에 의해 사용되거나, 유지되고 있는 변수 & 인터프리터가 강력하게 상호작용하는 함수에 접근할 수 있도록 해주는 모듈. 항상 사용 가능하다.

  1. 절대경로&상대경로의 차이점

절대경로 : 절대 경로는 기준점이 있기 때문에 헷갈릴 일이 없고, 순서대로만 경로를 입력해주면 된다. 하지만 하위 디렉토리로 많이 내려가거나, 파일 이름 자체가 길 경우 코드가 길어질 수 있다는 단점이 있다

상대경로 : 상대경로는 현재 위치를 기준으로 상위, 하위를 움직이기 때문에 보다 짧은 코드로 접근이 가능하지만, 경로 정의가 다소 헷갈릴 수 있고 파일이 위치한 경로가 바뀔 경우 재정의해야 한다는 단점이 있다.

  1. main.py 실습

  2. main.py에서 상대경로로 add_and_mutiply 를 임포트 했을 때 발생하는 에러를 확인

과제로 주어진 초기 상대경로
from .calculator.add_and_multiply import add_and_multiply

ImportError: attempted relative import with no known parent package
설치에러 : 알려지지않은 부모 패키지와 상대경로로 설치를 시도했다

.(dot)은 상대경로에서 현재 위치를 의미한다. calculator는 최상위 패키지다. 그러므로 상대경로에서 절대경로로 바꿔줘서 BIN(내 현재 프로젝트 디렉토리) 안에 calculator pacakge부터 순서대로 읽을 수 있게끔 만들어준다.



수정 후 함수실행 결과물로 정상적으로 5가 뜬다.

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



둘 다 에러가 발생한다. 심지어 첫 번째는 이미 정상적으로 main.py에서 구동이 됬을때 작성되어있던 코드다.

main.py에서 실행이 되었다는 것은 경로 자체는 문제가 없다는 의미다.
다만 실행은 되지않는다. 왜냐? dot이 들어간 순간 '실행'을 위해서는 해당 파일은 패키지를 인용해야 하기때문이다. 오케이! mainfile은 calculator란 패키지파일이 같은 디렉토리에 존재했다. 그러나 add_and_multiply는 실행을 위한 패키지가 존재하지않는다. 만일 실행가능한 python file을 만드려면 sub_calculator라든가 하는 패키지 안에 또다른 패키지 파일을 만들어야 되는 것이다. 그러므로 해당 파일에서 '실행'이 가능하게 하려면 패키지가 아닌 그냥 같은 디렉토리 안에 있는 모듈로써 multiplication을 import해야한다.


이런식으로 코드를 수정하면 에러없이 실행이 가능해진다.

  1. init file은 왜 생성해야 할까?

패키지 초기 파일이라는 의미를 가지고 있는 init file은 해당 디렉터리가 패키지의 일부임을 알려주는 역할을 한다. Python 3.3 버전부터는 init.py 파일이 없어도 패키지로 인식한다(PEP 420). 하지만 하위 버전 호환을 위해 init.py 파일을 생성하는 것이 안전한 방법이다.

profile
어려운 것은 없다, 다만 아직 익숙치않을뿐이다.

0개의 댓글