파이썬 기초 강좌 #20 모듈과 패키지 – 1편 (Module and Package – vol.1)

14 분 소요

안녕하세요. 스쿨오브웹의 이상희입니다. 이번 강좌에서는 모듈과 패키지에 대해서 공부하도록 하겠습니다.

모듈과 패키지가 어려운 개념이라고 생각하시는 분들이 많은데, 모듈과 패키지는 절대 어렵지 않습니다. 혹시라도 어렵다고 생각하고 계신다면 개념부터 사용방법까지 이 강좌에서 확실히 이해시켜 드리겠습니다.

먼저 모듈의 사전적 의미는 뭘까요? 네이버 영어사전에 모듈의 뜻은 이렇게 정의되어 있습니다.

  1. 모듈, 교과목 단위(특히 영국 대학에서 한 교육 과정의 일부가 되는 단위)
  2. 컴퓨터 모듈(특정 기능을 하는 컴퓨터 시스템이나 프로그램의 단위)
  3. 모듈, 조립 부품(기계·가구·건물 등을 구성하는 규격화된 부품)

이 정의들을 보면 모두 대상은 틀리지만 큰 시스템의 작은 파트나, 그안의 작은 부품을 말하고 있습니다. 프로그래밍에서의 모듈도 역시 같은 뜻을 가지고 있는데, 하나의 기능을 의미하거나, 특정 기능을 위해 만든 코드의 집합을 뜻하기도 합니다. 파이썬에서의 모듈은 파이썬 파일을 말합니다.

그럼 왜 모듈을 사용하는 걸까요? 컴퓨터를 살 때, 그래픽카드, CPU, 메모리가 머더보드에 붙어있는 일체형 모델이 좋을까요? 아니면, 머더보드와 분리되어 있는 모듈형 모델이 좋을까요? 일체형은 CPU 나 메모리가 고장 나면, 머더보드를 통째로 교체해야 하거나, 그냥 새 컴퓨터를 하나 사는 게 더 좋을 수도 있는데, 모듈형 컴퓨터는 고장 난 부품만 교체하면 되니까 모듈형 컴퓨터가 당연히 더 좋겠죠. 프로그램을 만들 때도 마찬가지입니다.

프로그램에서 모듈을 사용해야 하는 이유입니다.

  1. 기능별로 파일을 나눠놓으면 같은 기능이 필요한 다른 프로젝트에서 재활용할 수가 있습니다.
  2. 프로젝트가 커지면 커질수록 관리가 힘들어지는데, 프로젝트가 잘 모듈화 되어 있으면 관리가 쉬워집니다.
  3. 코딩하기가 쉬어집니다. 한 파일에 코드가 몇만 줄씩 들어가 있다고 생각해 보십시오. 아래위로 스크롤만 하다가 시간이 다 갈 겁니다.

파이썬을 설치하면 파이썬과 같이 설치되는 스탠더드 라이브러리가 있는데요, 이것들은 모두 모듈로 되어있고, 이 모듈들은 c 언어로 만들어진 빌트인 모듈과 파이썬으로 만들어진 일반 모듈로 나누어집니다. 파이썬에서 사용 가능한 모든 모듈을 확인하고 싶으시면 파이썬 공식 문서에서 확인하실 수가 있습니다.

파이썬 모듈 인덱스

여러분들이 항상 사용하는 프린트 함수나, 오픈 함수는 빌트인 모듈에 들어있고, 파이썬 인터프레터가 자동으로 임포트하기 때문에 따로 임포트를 하지 않아도 사용할 수가 있습니다. 빌트인 함수인 dir 함수를 사용하면, 현재 메인 네임스페이스에 저장된 오브젝트를 확인하실 수가 있습니다.

파이썬 코드
print(dir())
콘솔 출력
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

__builtins__ 라는 오브젝트가 보이실 텐데요, 이게 파이썬 인터프레터가 자동으로 임포트한 빌트인 모듈입니다. print 함수로 확인해 보죠.

파이썬 코드
print(__builtins__)
콘솔 출력
<module 'builtins' (built-in)>

빌트인 모듈이라고 출력되었는데요, 이 모듈 안에는 어떤 것들이 들어가 있는지 보도록 하죠.

파이썬 코드
print(dir(__builtins__))
콘솔 출력
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EncodingWarning', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

빌트인 모듈에는 우리가 자주 사용하는 print 함수나, 지금 사용한 dir 함수도 포함되어 있는데요, 빌트인 오브젝트를 통해서 print 함수를 실행해볼까요?

파이썬 코드
__builtins__.print('test')
콘솔 출력
test

dir 함수도 실행해 보겠습니다.

파이썬 코드
__builtins__.print(__builtins__.dir())
콘솔 출력
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

빌트인 모듈은 C 언어로 만들어져 컴파일 되어있기 때문에 소스코드를 직접 볼 수는 없습니다. 정말 궁금하신 분들은 github에서 C 언어로 만들어진 소스코드를 보실 수가 있습니다.

빌트인 모듈

그럼 이번에는 파이썬으로 만들어진 모듈들을 살펴 보도록 하겠습니다. 파이썬으로 만들어진 모듈들은 파이썬이 설치된 폴더안에 있는 Lib 이라는 폴더안에 있으니 구경하시고 싶으신 분들은 직접 파일을 열어 보십시오. 이 모듈들은 임포트 키워드를 사용해서 임포트를 한 뒤에 사용할 수가 있습니다.

Lib 폴더에 있는 pathlib 모듈을 임포트하고 출력해 보겠습니다.

파이썬 코드
import pathlib
 
print(pathlib)
콘솔 출력
<module 'pathlib' from 'C:\\Users\\CURTIS\\anaconda3\\envs\\aws\\Lib\\pathlib.py'>

빌트인 모듈과는 다르게 파일 경로가 출력되는 것을 알 수 있습니다.

inspect는 모듈은 getfile이라는 함수를 포함하고 있는데요, 이 함수를 사용해도 파일의 위치를 알 수가 있습니다.

파이썬 코드
import pathlib
import inspect
 
print(inspect.getfile(pathlib))
C:\Users\CURTIS\anaconda3\envs\aws\Lib\pathlib.py

그리고 getsource라는 함수를 사용하시면 실제 코드를 보실 수도 있습니다.

파이썬 코드
import pathlib
import inspect
 
print(inspect.getsource(pathlib))
콘솔 출력
import fnmatch
import functools
import io
import ntpath
import os
import posixpath
import re
...

파이썬 인터프레터는 import 키워드로 모듈을 임포트하면 sys.path 에 저장된 경로를 하나씩 뒤져서 모듈을 찾습니다. 이 경로들은 실행 파일이 있는 현재 폴더 ?? PYTHONPATH 환경변수에 저장된 경로 👉 파이썬 설치 경로의 순서로 되어있습니다. sys.path에 저장된 경로를 출력해 보겠습니다.

파이썬 코드
import sys
 
print(sys.path)
콘솔 출력
['C:\\Users\\CURTIS\\Dev\\Python\\aws', 'C:\\Users\\CURTIS\\anaconda3\\envs\\aws\\python311.zip', 'C:\\Users\\CURTIS\\anaconda3\\envs\\aws\\DLLs', 'C:\\Users\\CURTIS\\anaconda3\\envs\\aws\\Lib', 'C:\\Users\\CURTIS\\anaconda3\\envs\\aws', 'C:\\Users\\CURTIS\\anaconda3\\envs\\aws\\Lib\\site-packages']

그럼 지금부터 커스텀 모듈을 만들어 보겠습니다. 현재 폴더에 모듈 파일을 하나 생성하겠습니다. 일반적으로 모듈 파일의 이름은 모듈의 기능을 설명하는 이름을 많이 사용합니다. 게임에서 공격을 하는 기능이 들어간 모듈을 만들어 보죠. attack.py라는 파일을 만들고, 간단히 print 함수를 실행하는 코드를 입력하고 main.py 파일에서 사용해 보겠습니다.

attack.py
print('attack 모듈을 임포트 했습니다.')
main.py
import attack

단순히 임포트만하고 코드를 실행해 보겠습니다.

콘솔 출력
$ python main.py 
attack 모듈을 임포트 했습니다.

임포트만 했는데도 모듈안의 print 함수가 실행된 것을 볼 수 있습니다. 이 이유는 모듈이 임포트되면 현재 네임스페이스에 로딩이 되기 때문에 모듈안의 모든 코드가 실행되기 때문입니다.

이번에는 모듈안에 함수를 정의해보겠습니다.

attack.py
print('attack 모듈을 임포트 했습니다.')
 
def arrow():
    print('슈슈슉! 화살로 적에게 데미지를 50 주었습니다.')
 
def fireball():
    print('펑! 파이어볼로 적에게 데미지를 100 주었습니다.')

모듈을 임포트 하는 방법은 여러 가지가 있는데요, 모듈 안의 모든 코드를 임포트 하고 싶으면 아래 코드와 같이 임포트를 하시면 됩니다. 임포트를 하고 나면 네임스페이스에 어떤 것들이 저장되는지 살펴보겠습니다.

main.py
import attack
 
print(dir())
콘솔 출력
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'attack']

보시다시피 이렇게 임포트를 하시면 모듈 이름만 보이고 함수 이름은 보이지 않습니다. 모듈안의 함수를 사용하시고 싶으시면 모듈의 이름을 통해서 엑세스 하실 수 있습니다. 공격을 해보도록 하겠습니다.

main.py
import attack
 
attack.arrow()
attack.fireball()
콘솔 출력
$ python main.py 
attack 모듈을 임포트 했습니다.
슈슈슉! 화살로 적에게 데미지를 50 주었습니다.
펑! 파이어볼로 적에게 데미지를 100 주었습니다.

공격이 잘 되었습니다. 😁

가끔 이름이 아주 긴 모듈들이 있는데요, 매번 긴 모듈 이름을 입력하는게 귀찮다면, 아래 코드와 같이 as 키워드로 별명을 붙여서 사용하실 수도 있습니다.

main.py
import attack as att
 
att.fireball()
콘솔 출력
$ python main.py 
펑! 파이어볼로 적에게 데미지를 100 주었습니다.

별명도 귀찮다, 그냥 모듈 이름 자체를 쓰기 싫다고 하시는 분들은 from 키워드와 에스터리스크를 사용해서 임포트를 하시면 됩니다. from 키워드로 임포트를 하고 네임스페이스에 어떤게 저장되어 있는지 보겠습니다.

main.py
from attack import *
 
print(dir())
콘솔 출력
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'arrow', 'fireball']

이번에는 모듈이름은 보이지 않고 함수 이름만 저장되어 있는 것을 볼 수 있습니다. 이렇게 임포트를 하시면 바로 함수 이름을 사용해서 함수를 호출할 수가 있습니다.

main.py
from attack import *
 
arrow()
fireball()
콘솔 출력
슈슈슉! 화살로 적에게 데미지를 50 주었습니다.
펑! 파이어볼로 적에게 데미지를 100 주었습니다.

만약에 모듈의 사이즈가 크고, 몇개의 함수만 사용하고 싶으실 때는 필요한 함수만 임포트해서 사용하는게 효율적입니다.

main.py
from attack import arrow, fireball
 
arrow()
fireball()

모듈에 대한 공부는 여기서 마치고, 다음 영상에서 패키지에 대해서 공부해보겠습니다. 수고하셨습니다~! 👏

참고 링크: 파이썬 모듈 리스트