개요
본 글은 Python v3.13 공식 Tutorial의 6. Modules 의 모든 내용을 보다 이해하기 쉽게 정리한 글이다.
1. Module을 쓰는 이유
Interpreter에 직접 코드를 작성한 다음 interpreter를 종료하고 다시 켜면 definitions (함수와 변수들) 이 모두 사라진다. 그래서 더 긴 코드를 쓰려면 파일에 코드를 작성하고 interpreter에서는 그 파일을 실행하는 것이 좋다. 이 방식을 script라고 한다.
만일 코드가 더 길어진다면 유지보수를 위해 여러 파일로 쪼개고 싶어질 것이다. 그리고 한 파일에서 만든 함수를 매번 복붙하는게 아니라 그대로 가져오고 싶어지진다.
이것을 가능하게 하는 것이 Python의 module이다. module이라는 파일에 definitions를 모두 적어두면 다른 module이나 main module에서 import할 수 있다.
2. Module의 기본 개념
Module name은 파일명에서
.py 를 뺀 값이다. 또는 module 내의 global variable인 __name__에도 담겨있다. 예를 들어 IDE에서 아래와 같은 코드를 현재 경로에 fibo.py 로 저장한다면# Fibonacci numbers module def fib(n): """Write Fibonacci series up to n.""" a, b = 0, 1 while a < n: print(a, end=' ') a, b = b, a+b print() def fib2(n): """Return Fibonacci series up to n.""" result = [] a, b = 0, 1 while a < n: result.append(a) a, b = b, a+b return result
interpreter에서
import 를 통해 fibo module을 가져올 수 있다.>>> import fibo
그런데 이렇게 한다고 현재의 namespace에 곧바로 fibo module 내의 name들을 가져오는 것은 아니다.
fibo라는 module object만 추가 된 것이며 그 내부의 name을 가져오려면 . 을 통해 불러올 수 있다. >>> fibo.fib(1000) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 >>> fibo.fib2(100) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>> fibo.__name__ 'fibo'
local name에 할당한다면
. 없이도 사용 가능하다.>>> fib = fibo.fib >>> fib(500) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
3. Module의 심화 개념
3.1. 여러 가지의 import 방법들
3.1.1. from
module을 import할 때 module 내의 name을 현재 namespace에 곧바로 가져오고 싶을 수가 있다.
modname.itemname 으로 불러오는 것이 귀찮을 때는 아래처럼 from 을 쓸 수 있다.>>> from fibo import fib, fib2 >>> fib(500) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
이렇게 import를 하면
fibo 라는 name은 local namespace에 가져오지 않게 된다.또는
* 를 통해 module 내의 모든 name을 한 번에 가져올 수도 있다.>>> from fibo import * >>> fib(500) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
이때
_ 로 시작하는 name은 가져오지 않는다. * 를 쓰는 것은 추천하지 않는다. 왜냐하면 어떤 name을 가져왔는지 모르기 때문에 import한 name이 현재 namespace를 덮어 씌워 의도치 않은 결과를 가져올 수 있기 때문이다. interactive shell 같은 곳에서 일일이 typing하기 귀찮을 때 정도에만 쓰면 좋다.3.1.2. as
as를 통해 import하면 module을 as 뒤의 값으로 곧바로 할당시킨다. module name이 길 때 간략히 쓸 수 있어 유용하다. 그 외로는 모두 동일하게 동작한다.
>>> import fibo as fib >>> fib.fib(500) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
물론 from과 함께 쓸 수도 있다.
>>> from fibo import fib as fibonacci >>> fibonacci(500) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
3.2. Module을 Script로 실행하기
Script로 쓴다는 것은 터미널에서 .py 형식으로 파일을 실행한다는 의미이다.
아래와 같이 fibo라는 module을 터미널에서 실행하면 import한 것과 같이 execute되는데 이때 import와는 다른 점은
__name__이 “__main__” 으로 바뀐다는 것이다. $ python fibo.py <arguments>
그래서 module 맨 하단에 아래 코드를 추가하면
if __name__ == "__main__": import sys fib(int(sys.argv[1]))
해당 파일을 module로도 사용할 수 있으면서 script로도 사용할 수 있게 만들 수 있다.
예를 들어 아래와 같이 터미널에서 실행하면
__name__이 “__main__” 되므로 fib()가 실행되지만 $ python fibo.py 50 0 1 1 2 3 5 8 13 21 34
아래와 같이 module로 import하면
__name__은 여전히 “fibo” 이므로 코드가 실행되지 않는다.>>> import fibo >>>
module에 user interface를 추가하거나 debug 용도로 실행해봐야 할 때 유용하다.
3.3. Module의 실행 위치와 시점
module에 함수 정의, 변수 선언 등을 해두었을 것이다. module 내 global namespace에 있다고 해보자. module을 import한다면 import문에서 딱 한 번만 module 내 statements들이 실행된다. 그리고 그 name들은 그 module의 namespace 안에 들어가게 된다. 그렇기 때문에 현재 코드와 module 내 코드의 name이 서로 충돌 나는 일은 일어나지 않는다.
그렇기 때문에 function이나 class 내부에서 module을 import하면 module의 scope는 그 내부로 정해진다. 따라서 일반적으로는 import문들을 코드 맨 상위에 모두 모아 적는다. 이렇게 하면 module이 global scope에 존재해서 코드 어디서나 호출할 수 있기 때문이다.
또한 module 내 statements의 실행은 import에서 딱 한 번만 일어나기 때문에 module에서 무언가를 수정한게 있다면 즉각적으로 바뀌지 않는다. 그 이유는 module이 수 천 줄을 넘는 코드를 가지고 있을 때 매 import마다 다시 실행하면 성능이 매우 저하되기 때문이다. 그래서 첫 번째 import에서만 실행된다. 정확히는 import시
sys.modules 에 import할 module이 있는지 검색하고 없다면 module을 실행한 다음 sys.modules에 추가한다. 또는 이미 있다면 실행하지 않고 넘아간다. 만일 module의 변경 사항을 반영하고 싶다고 interpreter를 재시작하거나 아니면 importlib.reload() 을 쓸 수 있다. 아래와 같이 말이다.import importlib importlib.reload(modulename)
이때 주의해야할 점은 modulename만 reload가 가능하기 때문에 from을 통해 import를 했다면 module name이 현재 namespace에 없을 것이다. 따라서 from을 빼고 module만 import한 reload를 하고 나서 from으로 다시 module 내의 name을 import 해야한다.
import importlib import fibo importlib.reload(fibo) from fibo import fib
3.4. Module Search Path
module을 import될 때 그 module이 어떤 파일에 있는지 어떻게 찾는 걸까?
spam이라는 module을 import한다고 해보자. interpreter는 먼저
sys.builtin_module_names 에서 built-in module을 찾는다. 만일 찾지 못했다면 sys.path 에 기록된 directory의 list 내에서 spam.py를 찾는다. sys.path 는 아래 순서로 initialized 된다.- script가 존재하고 있는 directory
PYTHONPATH(Python 환경변수 경로)
- 외부 package가 설치되는 기본 경로 (pip 등으로 설치되는 경로)
이때 주의해야하는 사항이 있다.
sys.path initialization이 끝나고 난 이후에도 이 list를 코드가 동작 중일 때 수정할 수 있다. 위에서 말했듯 script가 있는 directory가 sys.path 가장 상위에 있기 때문에 standard library (일반적으로 많이 쓰는 다른 사람이 만든 라이브러리) 보다 우선 순위가 높다. 그래서 같은 name의 module이 현 directory에 있다면 의도치 않은 에러를 가져올 수 있다.3.5. Compiled Python Files
module의 loading 속도를 높이기 위해 module을 python 버전마다 미리 complie하고 cache로 저장해둔다.
__pycache__ directory 아래에 module.version.pyc 과 같이 만들어진다. 예를 들어 spam.py를 CPython 3.13으로 컴파일 했다면 pycache/spam.cpython-313.pyc 과 같이 저장된다.이때 원본 파일의 수정 일시와 compile된 버전을 자동으로 확인하고 complie이 오래됐다면 알아서 다시 compile한다. compile된 module은 platform과는 독립적이라서 다른 시스템에도 공유 가능하다.
2가지 상황에서는 cache를 확인하지 않는다.
- command line에서 직접 module을 불러왔다면 결과를 저장하지 않고 항상 다시 컴파일한다.
- 원본 코드 module이 없다면 cache를 확인하지 않는다. source code는 배포하지 않고 complie된 코드만 배포할 수 있는데, 이때는 compile된 module은 source directory 경로와 같은 곳에 있어야 하고 source module이 있으면 안 된다.
아래는 좀 더 심화 팁이다.
- Command line에서 python script를 실행할 때 뒤에
-0또는-00를 통해 compile된 module의 크기를 더 줄일 수 있다.-0은assert문을 삭제하고—00은 assert문과__doc__의 doctring을 둘 다 없앤다.
.pyc파일로 code를 실행하는 것과.py로 실행하는 것 간의 속도 차이는 전혀 없다..pyc로 빨라지는 것은 load 속도 뿐이다.
compileallmodule을 통해 어떤 directory 내의 모든 module의.pyc를 한 번에 만들 수 있다.
4. Standard Modules
Python 자체에서 제공하는 module을 standard module이라고 한다. language 자체의 core part는 아니지만 그럼에도 built in으로 있는 것들이다. 이런 것들은 platform마다 다른데 예를 들어
winreg 는 Windows 에서만 제공된다. sys 는 모든 Python interpreter에 들어가 있다.예를 들어 아래처럼 interpreter가 interactive mode일 때
sys.ps1, sys.ps2 는 primary, secondary prompt의 string을 정의한다.>>> import sys >>> sys.ps1 '>>> ' >>> sys.ps2 '... ' >>> sys.ps1 = 'C> ' C> print('Yuck!') Yuck! C>
sys.path 는 interpreter의 module search path를 담고 있다. 아래처럼 직접 수정할 수도 있다.import sys sys.path.append('/ufs/guido/lib/python')
5. dir() Funtion
built-in function인
dir()는 module이 정의하는 name을 알려준다. 호출하면 정렬된 string의 list를 return한다.>>> import fibo, sys >>> dir(fibo) ['__name__', 'fib', 'fib2'] >>> dir(sys) ['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework', '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook', 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info', 'warnoptions']
argument 없이는 현재 정의한 name을 보여준다.
>>> a = [1, 2, 3, 4, 5] >>> import fibo >>> fib = fibo.fib >>> dir() ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
dir()은 built-in function이나 variable은 보여주지 않는데 보고 싶다면 builtins module 내에 있다.
>>> import builtins >>> dir(builtins) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', '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']
6. Packages
6.1. Package의 기본 개념
Package는 “dotted module names” 를 통해 module의 namespace를 구조화하는 방법이다. 쉽게 말해 여러 module을 쉽게 관리할 수 있게 모은 것을 package라 한다. 예를 들어
A.B 는 A라는 package 안에 있는 B라는 submodule을 가리킨다. module과 같이 package도
. 으로 각 namespace가 구분되기 때문에 global variable name이 겹치는 걱정은 하지 않아도 된다.좀 더 구체적인 예시를 들어보자. 만일 여러 sound file과 sound data를 다루는 package를 만든다고 해보자. 첫 번째로
.wav, .aiff, .au와 같이 다양한 format을 관리해야한다. 그리고 sound data마다 echo나 equalizer와 같은 효과를 입히는 module도 필요하다. 이런 module들이 끝없이 추가될 것이다. 예를 들어 아래 파일 구조처럼 복잡해진다.sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for file format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ...
이러한 package를 import하면 Python은
sys.path 를 통해 package의 subdirectory를 찾는다.위 예시에서 각 package마다
__init__.py 가 보이는데 이 파일이 있어야 Python이 해당 directory를 package로 인식한다. 이게 필요한 이유는 package가 아니라 평범한 directory를 package로 인식해서 vaild한 module 대신 다른 name이 쓰일 수 있기 때문이다. __init__.py 는 비워둘 수도 있지만 package의 initialization code를 실행시키게 할 수도 있다.6.2. Import Package
package 내의 어떤 module을 import하고 싶다면 아래처럼 할 수 있다. 이렇게 하면
sound.effects.echo 라는 이름의 submodule을 import한 것이다. 사용시 반드시 full name을 적어야 한다.import sound.effects.echo sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
또는 아래처럼 import하면 package prefix 없이 사용할 수 있다.
from sound.effects import echo echo.echofilter(input, output, delay=0.7, atten=4)
다른 방법으로는 아래처럼 하면 function이나 variable을 곧바로 사용할 수 있다.
from sound.effects.echo import echofilter echofilter(input, output, delay=0.7, atten=4)
즉,
from package import item 형식을 사용할 때 item은 submodule일 수도 있고 subpackage일 수도 있고 아니면 그 안의 fucntion, class, variable과 같은 name일 수도 있는 것이다. 이때의 원리는 import 다음의 item이 package 내에 정의됐는지 확인을 한다. 정의되지 않았다면 item을 module로 간주하고 load하려 한다. 만일 실패한다면 ImportError가 raise된다.반대로
import item.subitem.subsubitem 과 같은 형식에서는 가장 마지막에 등장하는 subsubitem은 module이거나 package일 수 있지만 subitem에서 정의된 name일 수는 없다. 물론 마지막 이전의 item, subitem은 package여야만 한다.6.3. Import * From a Package
Package를 사용하는 user가
from sound.effects import * 를 통해 import한다면 어떤 일이 일어날까? package 내에 존재하는 모든 submodule.py들을 알아서 import할 것이라고 기대하지만 사실은 그렇지 않다. 그렇게 하면 모든 submodule을 import하는데 시간이 오래 걸리고 예기치 않은 부작용이 나타날 수도 있기 때문이다.그렇기에 package를 만드는 author는 명확한 index를 제공할 수 있다. package의
__init__.py 에서 __all__ 이라는 list를 정의해두는 것이다. 여기서 from package import * 문을 만났을 때 import 돼야 하는 모든 module name을 기록해두면 된다. author가 매번 업데이트를 해줘야하므로 이런 방식이 안 쓰일 것 같다면 적어두지 않을 수도 있다.예시를 살펴보자. 만일
sound/effects/init.py 에 아래처럼 코드를 둔다면 from sound.effects import * 를 했을 때 3개의 submodule만 import할 것이다.__all__ = ["echo", "surround", "reverse"]
여기서 주의해야할 점은
__init__.py 에 정의된 local name에 의해 submodule이 가려질 수도 있다는 것이다. 아래 예시처럼 reverse라는 function이 local name에 있으므로 reverse라는 submodule이 import되지 않고 function이 불러와 질 것이다.__all__ = [ "echo", # refers to the 'echo.py' file "surround", # refers to the 'surround.py' file "reverse", # !!! refers to the 'reverse' function now !!! ] def reverse(msg: str): # <-- this name shadows the 'reverse.py' submodule return msg[::-1] # in the case of a 'from sound.effects import *'
만일
__all__ 이 정의되지 않았다면 from sound.effects import * 은 sound.effects 내의 모든 submodule을 import 하지 않는다. 그저 __init__.py 내의 name들만 가져올 뿐이다. 물론 명확하게 지정한 것들은 제대로 import한다. 아래 코드에서 echo와 surround submodule은 제대로 import되는 것이다.
import sound.effects.echo import sound.effects.surround from sound.effects import *
그렇기 때문에
import * 를 production code에서 쓰는 것은 안 좋은 습관이다. 명확하게 명시해서 import하는 것이 좋다.6.4. Intra-package References
subpackage 구조가 있을 때 지금은 계속해서 absolute import를 보이고 있었다. 예를 들어
sound.filters.vocoder submodule에서 sound.effects.echo module이 필요하다면 from sound.effects import echo 를 통해 import하는 것이다.Python에서는 relative import도 지원한다. 아래 예시를 살펴보자.
from . import echo from .. import formats from ..filters import equalizer
해당 코드가 sound.effect.surround module에 정의되어 있다고 해보자.
from 에서 . 하나는 현재 module이 위치한 package를 의미한다. .. 은 부모 package가 된다. 그래서 ..filters 는 부모 package인 sound의 module인 filters를 찾을 수 있는 것이다.한 가지 주의해야할 점은 relative import가 현재 module의 package name을 기반으로 한다는 것이다. 그래서 main module은 package를 가지지 않으므로 이때는 항상 absolute import를 사용해야 한다.
6.5. Packages in Multiple Directories
Package는
__init__.py 내의 코드가 실행되기 전에 그 package의 directory를 담은 string list를 __path__ 에 담아둔다. __init__.py 에서 이 값을 수정할 수 있는데 이를 통해 subpackage와 module을 찾을 때 영향을 주어 여러 경로에서부터 package를 추가할 수 있다.Reference
[1] Python Software Foundation. "6. Modules" The Python Tutorial, version 3.13, Python Software Foundation, 2024, https://docs.python.org/3.13/tutorial/modules Accessed 26 July 2025.
