# module.py
= 37
a def func():
print(f'func says that a is {a}')
class SomeClass:
def method(self):
print('method says hi')
print('loaded module')
14 모듈(modules
)과 패키지(packages
)
- 『Python Distilled(David M. Beazley)』의 Chapter 8 Modules and Packages를 읽자. 이 글의 내용은 주로 이 책을 참고하였다
- 『Python in a Nutshell, 4th Edition』 Chapter 7. Modules and Packages
14.1 모듈과 import 문
모든 파이썬 소스 파일은 모듈이 될 수 있다. 이를테면 다음과 같은 소스 코드를 가진 module.py
라는 파일이 있다고 생각해 보자. 이 파일은 글로벌 변수(아래 내용 참고) a
, 함수 func
, 클래스 SomeClass
, 그리고 print
실행문을 가지고 있다.
이 모듈을 임포트할 때는 import module
을 사용한다.
import module
loaded module
모듈을 로딩하면 이 이름을 가지고 모듈의 값이나 함수, 클래스 등을 사용할 수 있게 된다.
module.a
37
module.func()= module.SomeClass()
s s.method()
func says that a is 37
method says hi
- In Python, global variables are not global to all modules, but rather are attributes of a single module object.
14.1.1 import module
실행되는 과정
import module
를 실행시키면 여러 가지 과정들이 진행된다.
module.py
소스 파일을 찾는다. 파이썬 인터프리터가 모듈을 찾는 것은sys.path
에 정의된 디렉터리 순서에 따른다. 찾지 못하면ImportError
예외가 발생한다.- 새로운 모듈 객체(module object)가 생성된다. 이 객체는 모듈에 포함된 모든 글로벌 정의들을 담는 컨테이터 역할을 한다. 이것을
namespace
라고 부른다. - 모듈 소스 코드가 새롭게 생성된 module namespace에서 실행된다.
- 에러가 발생하지 않으면 새롭게 만든 모듈 객체에 대한 이름이 부른 장소에 생성된다.
module.py
인 경우 그 이름은module
이 된다.
import
문은 모듈을 로딩하면서 안의 소스 코드를 모두 실행한다. 그래서 print('loaded module')
문이 실행된다. 그리고 모듈에서 정의되는 함수, 클래스 등은 모듈 namespace에 저장한다.
여러 개의 모듈들을 한꺼번에 로딩할 때는 모듈 이름을 코마로 구분한다.
import os, re
모듈의 이름을 바꾸어 사용하고자 하는 경우에는 끝에 as
구에 사용할 이름을 추가한다.
import module as mo
이름을 값에 연결하는 과정을 binding이라고 한다. =
을 사용한 할당, def
를 이용한 함수 정의, class
를 이용한 클래스 정의와 마찬가지로 import
문도 역시 모듈 객체를 어떤 이름에 대응시키는 binding 과정의 한 종류이다.
14.2 모듈 캐싱: sys.modules
딕셔너리
파이썬 모듈의 소스 코드는 로딩되어 딱 한번만 로딩된다.
따라서, 모듈을 import 하고 나서, 모듈의 소스 코드가 변경된 다음, 다시 import
를 실행해도 바뀐 모듈이 로딩되지 않는다.
첫 번째 import
문이 실행할 때 만든 모듈 객체를 sys.modules
라는 딕셔너리에 캐싱한다. 이 딕셔너리의 키는 모듈 이름이고 대응하는 값은 모듈 객체이다. 참고로 현재 세션에서 로딩된 모듈의 개수을 보자.
import sys
len(sys.modules.keys())
1769
import
문을 사용하여 원래의 모듈을 다시 로딩하려고 시도해도, 이 경우는 원래의 소스 코드를 가지고 로딩 작업을 하지 않고, 캐싱되어 있는 sys.modules
에서 모듈 객체를 가지고 온다.
14.3 from
문
from
문은 모듈에서 특정 이름(정의)만 로딩하는 데 사용된다.
from module import func
from module import name
은 모듈 cache에 저장된 이름을 현재의 namespace로 가지고 온다. 즉, 먼저 import module
을 실행하고 나서, cache
에 저장된 것을 가지고 온다.
14.4 모듈 컴파일
어떤 모듈이 처음으로 파이썬으로 임포트될 때, 모듈의 소스 코드가 파이썬 인터프리터에 의해서 bytecode로 컴파일되고, 이 컴파일된 코드는 __pycache__
라는 디렉터리 안에 .pyc
라는 파일로 저장된다. 보통 .py
파일과 같은 위치에 저장된다.
이런 상태에서 다른 파이썬 프로그램에서 이 모듈을 임포트할 경우, 이 컴파일된 bytecode가 로딩된다. 따라서 import
과정이 빨라진다.
따라서, 가상 환경을 만들고 numpy
등의 모듈을 설치한 후 처음 사용할 때 컴파일되기 때문에 시간이 좀 걸린다. 하지만 다음에 또 사용하는 경우에는 bytecode가 바로 로딩되기 때문에 시간이 빨라진다.
14.5 sys.path
우리가 어떤 파이썬 소스를 모듈로 사용하려 한다고 해 보자. 이 소스 파일을 어디에 두어야 파이썬 인터프리터가 인식할까?
sys.path
는 파이썬 리스트인데, 여기에는 현재의 파이썬 인터프리터가 모듈을 찾을 때 사용하는 디렉터리들이 순서대로 들어있다. 보통의 Python REPL에서는 첫 번째는 빈문자열인데, 이것은 현재의 디렉터리를 의미한다.
모듈이 들어 있는 경로를 sys.path
리스트에 append()
메서드 등으로 추가하면 해당 모듈을 불러올 수 있다.
import sys
sys.path
['/Users/seokbumko/Learning/Education/cds-python',
'/opt/anaconda3/lib/python312.zip',
'/opt/anaconda3/lib/python3.12',
'/opt/anaconda3/lib/python3.12/lib-dynload',
'',
'/opt/anaconda3/lib/python3.12/site-packages',
'/opt/anaconda3/lib/python3.12/site-packages/aeosa',
'/opt/anaconda3/lib/python3.12/site-packages/setuptools/_vendor']
14.6 if __name__ == "__main__":
모든 모듈 객체는 __name__
attribute에 모듈의 이름(문자열)을 저장한다. 파이썬 최상위 모듈의 __name__
의 값은 "__main__"
이다. 이 정보를 활용하여, 어떤 모듈이 (1) 다른 모듈로 임포트될 때와 (2) 하나의 독립된 스크립트로 실행될 때 다르게 실행되게 만들 수 있다. 보통 다음과 같이 코딩한다.
if __name__ == '__main__':
# Yes. 메인 스크립트로 실행
statementselse:
# No, 모듈로 임포트될 때 실행
statements
14.7 패키지(package
): 모듈의 모음
패키지는 나무 같은 위계 구조로 되어 있는 모듈의 집합이다. 패키지는 __init__.py
라고 하는 파일을 가진 디렉터리에 만든다. 여기에 파이썬 소스 파일이나 서브패키지를 배치한다.
다음은 graphics
라는 패키지를 만든 예이다.
graphics/
__init__.py
primitive/
__init__.py
lines.py
fill.py
text.py
...
graph2d/
__init__.py
plot2d.py
...
graph3d/
__init__.py
plot3d.py
...
formats/
__init__.py
gif.py
png.py
tiff.py jpeg.py
import
문에서는 .
을 사용하여 이 위계를 표현한다. 다음은 그 예이다.
import graphics.primitive.fill from graphics.primitive import fill