[TIL]Python. Modules 2

여주링·2020년 11월 26일
0

Python

목록 보기
13/15
post-thumbnail

Module&Package 검색방법

Import Search 순서

abc 라는 package가 있다고 가정해보자

import abc

abc라는 package를 Import했다. 그럼 파이썬은 어디서 이 package를 찾을까?

파이썬은 다음 3가지 장소를 순서대로 보며 검색한다
1. sys.modules
2. built-in modules
3. sys.path

1.sys.modules(dictionary로 구성)

파이썬이 모듈이나 package를 찾기위해 가장 먼저 확인하는 곳
파이썬에는 이미 import된 모듈과 package들은 다시 찾지 않아도 되도록 하는 기능을 가지고 있음. (엄청 편리한 기능!)
이는 반대로 아직 import되지 않고 새로 import 하는 모듈은 sys.modules 에서 찾을 수 없다는 걸 의미한다.

abc package가 이전에 사용된적이 없다? 그럼 sys.modules에는 없는것!

2.built-in modules

파이썬에서 제공하는 파이썬 공식 라이브러리들이 포함되어있다.Built-in 모듈들은 이미 파이썬에 포함되어 나오므로 파이썬이 쉽게 찾을 수 있다.

3.sys.path(string요소를 가진 list)

마지막으로 보는 장소가 바로 sys.path이다
sys.path는 기본적으로 list이며 string 요소들을 가지고 있는 list로
각 요소들은 다음 처럼 경로를 나타낸다

['',
'/Users/song-eun-u/anaconda3/bin',
'/Users/song-eun-u/anaconda3/lib/python36.zip',
'/Users/song-eun-u/anaconda3/lib/python3.6', 
'/Users/song-eun-u/anaconda3/lib/python3.6/lib-dynload', 
'/Users/song-eun-u/anaconda3/lib/python3.6/site-packages', '/Users/song-eun-u/anaconda3/lib/python3.6/site-packages/aeosa', 
'/Users/song-eun-u/anaconda3/lib/python3.6/site-packages/IPython/extensions', 
'/Users/song-eun-u/.ipython’]

파이썬은 list의 각 경로를 하나 하나 체크하며 해당 경로에 import 하고자 하는 package가 위치해 있는지 확인한다.

참고로 sys는 파이썬에 포함되어 있는 모듈이다.

그러므로 sys 모듈을 import 해서 sys.modules와 sys.path를 출력이나 수정하는게 가능하다!

import sys

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

sys 모듈의 위치를 어떻게 찾을 수 있을까?

sys.modules는 파이썬이 module과 package를 찾기 위해 가장 먼저 둘러보는 곳으로 이미 built-in된 상태기 때문에, built-in module이 들어있는 부분에서 찾게 된다.

'sys': <module 'sys' (built-in)>

정리

파이썬은 import 하고자 하는 모듈과 package를 찾을때에 먼저 sys.modules를 보고, 없으면 파이썬 built-in 모듈들을 확인 하고 마지막으로 sys.path에 지정되어 있는 경로들을 확인해서 찾는다.
sys.path 에서도 못찾으면 ModuleNotFoundError 에러를 리턴한다.

Absolute Path & Relative Path

파이썬의 built-in 모듈과 pip 을 통해 설치한 외부 모듈 및 package는 일반적으로 import 하는데 큰 문제가 되지 않는다.
문제는 직접 개발한 local package다. 내가 직접 개발한 local package를 import 할때는 해당 package의 위치에 맞게 import 경로를 잘 선언해야 한다는 것이다. local package의 경로에 관한 개념이 Absoluate/Relative이다

Absolute path(절대경로)

Absolute path는 import를 하는 파일이나 경로에 상관없이 항상 경로가 동일한 경로를 뜻한다. 절대 파일 경로는 파일 시스템의 루트에서 시작하여 주어진 파일 또는 디렉토리에 액세스하는 방법으로 슬래시 또는 드라이브 레이블로 표시 된다. 

└── my_app
	├── main.py 
	├── package1 
	│ 	├── module1.py 
	│ 	└── module2.py 
	└── package2 
		├── --init--.py 
		├── module3.py 
		├── module4.py 
		└── subpackage1 
			└── module5.py

위의 예시에는 my_app라는 프로젝트가 있고 package1과 package2 라는 2개의 package을, 그리고 package2는 subpackage2 라는 중첩 package을 가지고 있다.

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

이를 운영체제별로 표시하면
<리눅스>my_app/package2/subpackage1/module5.py
<윈도우>my_app\package2\subpackage1\module5.py
<Python>my_app.package2.subpackage1.module5.py (/\대신에 .을 통해서 경로를 표현)

이미 my_app 프로젝트 안에 있으므로 my_app 은 생략되며 결과적으로는 다음처럼 경로를 표현하게 된다
<import사용시>

package2.subpackage1.module5.py

<from/import 사용시>

from package2.subpackage1.module5 import function2

my_app 프로젝트 내에서는 어느 파일, 어느 위치에서 import 하던지 경로가 항상 위와 같이 동일하게 되므로 absolute path 라고 하는 것~

참고로 현재의 프로젝트 디렉토리는 default로 sys.path 에 포함되게 된다. 그러므로 absolute path는 current directory 로 부터 경로를 시작하게 된다. 일반적으로 local package를 import 할때는 absolute path를 사용하면 됩니다.

단점?

경로가 길어질수 있다

Relative path

Absolute path의 경우 경로가 길어질수 있다는 단점이 존재하는데, 이를 보안하기위해 사용.

‘absolute path :프로젝트의 최상단 디렉토리를 기준으로 경로를 잡음’
-> relative path는 import 하는 위치를 기준으로 경로를 정의한다.
그래서 일반적으로 relative path는 local package 안에서 다른 local package를 import 할때 사용된다.

└── my_app
	├── main.py 
	├── package1 
	│ 	├── module1.py 
	│ 	└── module2.py 
	└── package2 		#첫번째, class1을 import하겠다
		├── __init__.py 
		├── module3.py  #여기서
		├── module4.py 
		└── subpackage1 
				└── module5.py #두번째, 여기의 function2를 import하겠다

package2의 module3에서 package2의 class1과 package2의 하위 package인 subpackage1의 module5의 function2 함수를 import하려고 하면 다음 처럼 하면 된다

# package2/module3.py
 from . import class1 
 from .subpackage1.module5 import function2

여기서 dot(.)은 import가 선언되는 파일의 현재 위치를 의미한다.
현재위치는 package2/module3.py 이므로 현재 위치에서부터 원하는 모듈의 경로만 선언해주면 되는 것.

그리고dot 2개도 있다. dot 2개(..) 는 현재위치에서 상위 디렉토리로 가는 경로로써 사용된다.

단점?

Relative path는 선언해야 하는 경로의 길이를 줄여준다는 장점은 있지만 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점이 있다. 그러므로 웬만한 경우 absolute path를 사용하는게 권장 된다.

실전 연습

calculator 패키지 만들기

실제로 이런 구조로 내 노트북에 만드는 작업을 했다.

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

상대경로로 add_and_mutiply를 Import를 하니
ImportError: attempted relative import with no known parent package라는 에러가 발생한다

from .calculator.add_and_multiply import add_and_multiply

if __name__ == '__main__':
    print(add_and_multiply(1,2))
    print(__name__)
    print("ok")

위의경우 main.py에서 import를 하는데 calculator 파일구조에서 main은 package에 속한 파일이 아닙니다. main 모듈의 이름은 항상 "main"이므로 Python 애플리케이션의모듈로 사용하려는 모듈은 항상 절대 가져 오기를 사용해야한다는 룰이 있다.
->main.py에서 상대경로로 Import를 했으니 당연히 안되는것! 절대경로로 바꿔주자

2.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)
print(add_and_multiply(2,5))

<상대경로로 안되는 이유>
위에서 만든 예시는 하나의 프로젝트안에 있지만, 같은 Package는 아니다. 따라서 main.py에 상대경로로 접근해서 import하는것이 불가능하다.

내가 실행시키고 있는 파일은 add_and_multiply.py인데,
calculator 폴더 안에 multiplication.py도 함께 있다.
같은 상위 calculator에 포함되어있기 때문에 절대경로로 처리하면 문제가 발생하지 않는다.

profile
🌱Backend Developer👩‍💻

0개의 댓글