모듈과 표준 라이브러리
파이썬에는 바로 사용할 수 있는 방대한 도구 모음이 함께 제공됩니다. 난수, 수학 연산, 날짜, 파일 경로 등등 정말 많습니다. 이런 도구들은 모듈이라는 형태로 존재하며, import를 통해 여러분의 코드로 가져올 수 있습니다. 파일과 예외 챕터에서 이미 import json을 사용해본 적이 있습니다. 이번 챕터에서는 import를 제대로 다루고, 표준 라이브러리에서 가장 유용한 부분들을 소개합니다.
모듈 import하기
가장 간단한 import는 모듈 전체를 가져와서 점(dot) 표기법으로 그 안의 내용을 사용할 수 있게 해줍니다. 특정 이름만 모듈에서 가져와 접두사 없이 바로 사용할 수도 있습니다. 별칭(alias)을 쓰면 긴 이름을 줄일 수 있습니다.
import math
math.sqrt(16) # 4.0
math.pi # 3.141592653589793
math.floor(3.9) # 3
math.ceil(3.1) # 4모듈에서 특정 이름만 import해서 바로 사용할 수도 있습니다:
from math import sqrt, pi
sqrt(16) # 4.0 ("math." 접두사 필요 없음)
pi # 3.141592653589793모듈이나 이름에 별칭을 붙여 줄여 쓸 수도 있습니다:
import math as m
m.sqrt(16) # 4.0
from math import sqrt as square_root
square_root(25) # 5.0별칭은 인기 있는 서드파티 라이브러리에서 흔히 사용됩니다(import numpy as np, import pandas as pd). 표준 라이브러리 모듈은 전체 이름을 그대로 쓰는 편이 코드를 더 읽기 쉽게 만듭니다.
import math는 모듈 전체를 가져오고, 점을 찍어서 math.sqrt(16)처럼 안으로 들어갑니다. from math import sqrt는 이름 하나만 가져와서 접두사를 뗄 수 있게 해줍니다. as 키워드는 긴 import 이름을 바꿔주지만, 표준 라이브러리라면 전체 이름을 쓰는 편이 더 읽기 쉽습니다. random
random 모듈은 난수를 생성하고 무작위 선택을 수행합니다. 게임, 시뮬레이션, 무작위 표본 추출 등 예측 불가능성이 필요한 곳이라면 어디든 사용할 수 있습니다. **시드(seed)**를 설정하면 결과를 재현할 수 있습니다. 같은 시드는 매번 같은 결과 순서를 만들어냅니다.
import random
random.random() # 0과 1 사이(1 제외)의 실수
random.randint(1, 10) # 1부터 10 사이의 정수 (양쪽 다 포함)
random.uniform(1.0, 10.0) # 1.0과 10.0 사이의 실수
colours = ["red", "green", "blue"]
random.choice(colours) # 항목 하나를 선택
random.choices(colours, k=3) # k개 선택 (중복 허용)
random.sample(colours, k=2) # k개 선택 (중복 없음)
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers) # 제자리에서 섞고, None을 반환재현 가능한 결과가 필요할 때(테스트나 데이터 과학에서 유용합니다), 생성 전에 시드를 설정하세요:
random.seed(42)
random.randint(1, 100) # 시드 42라면 항상 같은 값같은 시드는 어떤 컴퓨터에서도 매번 같은 결과 순서를 만듭니다.
random.choice()는 리스트에서 항목 하나를 고르고, random.randint(1, 10)은 범위 안(끝값 포함)의 정수를 줍니다. 실행할 때마다 같은 결과를 얻고 싶다면(예: 테스트), 먼저 random.seed()를 호출하면 순서가 고정됩니다. math
math 모듈은 숫자 챕터에서 다룬 기본 산술 연산자를 넘어서는 더 고급 수학 연산을 추가로 제공합니다. 제곱근, 거듭제곱, 로그, 삼각함수, pi나 무한대 같은 특수한 값들이 모두 여기 있습니다.
import math
math.sqrt(25) # 5.0
math.pow(2, 10) # 1024.0 (2 ** 10과 같지만 항상 float를 반환)
math.log(100, 10) # 2.0 (밑이 10인 로그)
math.log(math.e) # 1.0 (자연로그)
math.sin(math.pi / 2) # 1.0
math.cos(0) # 1.0
math.ceil(3.2) # 4
math.floor(3.9) # 3
math.trunc(3.9) # 3 (양수에서는 int()와 동일)
math.inf # 무한대
math.isnan(float("nan")) # True
math.isinf(math.inf) # Truemath는 + - * /를 넘어서는 모든 것, 제곱근, 거듭제곱, 로그, 삼각함수, 거기에 math.pi 같은 상수까지 다룹니다. 계산이 기본 연산자만으로 부족하다 싶으면 바로 이걸 쓰세요. 그러면 공식을 직접 만들 필요가 없습니다. datetime
datetime 모듈은 날짜와 시간을 다룹니다. datetime.now()는 현재 날짜와 시간을 줍니다. strftime()은 그것을 문자열로 포맷하고, strptime()은 문자열을 datetime으로 파싱합니다. timedelta는 더하거나 뺄 수 있는 기간을 나타냅니다.
from datetime import datetime, date, timedelta
now = datetime.now() # 현재 날짜와 시간
today = date.today() # 현재 날짜만
print(now.year, now.month, now.day)
print(now.hour, now.minute, now.second)
# 포맷팅
print(now.strftime("%Y-%m-%d")) # "2024-01-15"
print(now.strftime("%d %B %Y, %H:%M")) # "15 January 2024, 09:42"
# 파싱
deadline = datetime.strptime("2024-12-31", "%Y-%m-%d")
# 산술 연산
tomorrow = today + timedelta(days=1)
next_week = today + timedelta(weeks=1)
diff = deadline - now
print(f"{diff.days} days until deadline")자주 쓰는 strftime 코드:
| 코드 | 의미 | 예시 |
|---|---|---|
%Y | 4자리 연도 | 2024 |
%m | 월 (0으로 패딩) | 01 |
%d | 일 (0으로 패딩) | 15 |
%H | 시 (24시간) | 09 |
%M | 분 | 42 |
%B | 전체 월 이름 | January |
datetime.now()는 현재 날짜와 시간을 줍니다. strftime()은 %Y-%m-%d 같은 코드로 문자열로 바꿔주고, strptime()은 그 반대로 문자열을 다시 파싱합니다. timedelta는 더하거나 뺄 수 있는 기간이라, today + timedelta(days=1)는 내일입니다. os와 pathlib
pathlib은 파일 경로를 다루는 현대적인 방법입니다. Path 객체는 / 연산자를 사용해 경로를 만들고, 살펴보고, 탐색할 수 있게 해줍니다. os는 환경 변수와 더 낮은 수준의 OS 작업에 접근할 수 있게 해줍니다. 새 코드에서는 pathlib을 우선적으로 사용하세요.
from pathlib import Path
p = Path("data/reports")
p.exists() # 경로가 존재하면 True
p.is_dir() # 디렉터리면 True
p.is_file() # 파일이면 True
p.mkdir(parents=True, exist_ok=True) # 디렉터리 생성
for f in p.glob("*.csv"): # 디렉터리 안의 모든 CSV 파일
print(f.name) # 파일 이름
report = p / "report_jan.csv" # / 연산자로 경로 연결
report.stem # "report_jan" (확장자 없는 이름)
report.suffix # ".csv"
report.parent # Path("data/reports")
content = report.read_text() # 파일 내용을 바로 읽기
report.write_text("new content\n") # 바로 쓰기os 모듈의 경우:
import os
os.getcwd() # 현재 작업 디렉터리
os.listdir(".") # 디렉터리 내용 나열
os.path.exists("data.txt") # 경로가 존재하면 True
os.path.join("data", "file.txt") # "data/file.txt" (크로스 플랫폼)
os.environ.get("HOME") # 환경 변수 읽기새 코드에서는 pathlib을 우선적으로 사용하세요. 환경 변수가 필요하거나 문자열을 기대하는 오래된 API를 다룰 때는 os를 사용하세요.
pathlib.Path는 파일 경로를 만지작거릴 수 있는 객체로 다룹니다: .exists(), .read_text(), .write_text(), .glob(). / 연산자는 조각들을 이어붙여서, p / "report.csv"가 실제 경로처럼 읽힙니다. 새 코드에서는 더 오래된 os.path보다 pathlib을 쓰세요. timeit
timeit은 코드가 실행되는 데 걸리는 시간을 측정합니다. 두 가지 방식을 비교해서 더 빠른 쪽을 고르고 싶을 때 유용합니다. 안정적인 측정값을 얻으려면 코드를 여러 번 실행하세요.
import timeit
# 문장 하나의 시간 측정
timeit.timeit("sum(range(1000))", number=10000)
# 더 복잡한 블록의 시간 측정
setup = "data = list(range(1000))"
code = "[x * 2 for x in data]"
time = timeit.timeit(code, setup=setup, number=10000)
print(f"{time:.4f} seconds for 10,000 runs")number는 몇 번 반복할지를 나타냅니다. 반복을 늘릴수록 측정값이 더 안정적입니다.
timeit.timeit()은 코드를 반복해서 실행하며 시간을 재고, 그 값을 실행 횟수로 나누면 안정적인 호출당 수치가 나옵니다. "이 둘 중 어느 게 더 빠른가"를 짐작이 아니라 확인하는 방법입니다. 한 번의 실행은 너무 들쭉날쭉해서 믿을 수 없으니 반복시키세요. string
string 모듈은 문자, 숫자, 구두점을 위한 미리 만들어진 문자열 상수를 제공합니다. 문자를 검사하거나 특정 알파벳으로 무작위 문자열을 생성해야 할 때 유용합니다.
import string
string.ascii_lowercase # "abcdefghijklmnopqrstuvwxyz"
string.ascii_uppercase # "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
string.ascii_letters # 둘을 합친 것
string.digits # "0123456789"
string.punctuation # 모든 구두점 문자문자를 검사하거나 무작위 문자열을 생성할 때 유용합니다:
import string, random
chars = string.ascii_letters + string.digits
password = "".join(random.choices(chars, k=12))string 모듈은 미리 만들어진 문자 집합을 건네줍니다: string.ascii_letters, string.digits, string.punctuation. 이것들은 그냥 문자열이라, 반복하거나 표본을 뽑을 수 있습니다. 이미 알고 있는 알파벳을 기준으로 뭔가를 만들거나 검사해야 할 때 편리합니다. 나만의 모듈 만들기
파이썬 파일은 그 자체로 모듈입니다. 다른 파일에서 사용하려면 확장자 .py를 뺀 파일 이름으로 import하면 됩니다. 모듈 전체를 import해서 점 표기법으로 내용을 사용할 수도 있고, 특정 이름만 직접 import할 수도 있습니다.
# utils.py
def clamp(value, lo, hi):
return max(lo, min(value, hi))
PI = 3.14159# main.py
import utils
utils.clamp(150, 0, 100) # 100
utils.PI # 3.14159
from utils import clamp
clamp(50, 0, 100) # 50파이썬은 import하는 파일과 같은 디렉터리(그리고 몇 군데 다른 위치)를 살펴봐서 모듈을 찾습니다. 규모가 큰 프로젝트에서는 모듈을 패키지로 구성합니다. 패키지는 __init__.py 파일이 있는 디렉터리입니다.
.py 파일이든 이미 모듈입니다. .py를 뺀 파일 이름으로 import하면, 그 안의 함수와 변수를 그대로 쓸 수 있습니다. import utils 다음 utils.clamp(...), 아니면 from utils import clamp로 이름 하나만 가져올 수도 있습니다. 이렇게 커지는 프로그램을 여러 파일로 나눕니다. __name__ == "__main__"
파이썬이 파일을 직접 실행하면 __name__은 "__main__"으로 설정됩니다. 같은 파일이 모듈로 import되면 __name__은 모듈 이름이 됩니다. 이 패턴을 이용하면 파일을 직접 실행할 때만 실행되고, 다른 모듈이 import할 때는 건너뛰는 코드를 작성할 수 있습니다.
# utils.py
def clamp(value, lo, hi):
return max(lo, min(value, hi))
if __name__ == "__main__":
# 이 코드는 python utils.py로 실행할 때만 동작합니다
# import utils로 가져올 때는 실행되지 않습니다
print(clamp(150, 0, 100)) # 100독립 실행 스크립트로도 쓸모 있는 모듈이라면 이 패턴이 표준입니다.
if __name__ == "__main__":은 하나의 파일이 import 가능한 모듈이면서 동시에 실행 가능한 스크립트가 되게 해줍니다. 가드 안의 코드는 python utils.py로 실행할 때 동작하고, 다른 파일이 import utils할 때는 건너뜁니다. 모듈 맨 아래에 간단한 데모나 테스트를 붙여둘 때 편리합니다. 표준 라이브러리 핵심 기능
알아두면 좋은 모듈 몇 가지가 더 있습니다. 각각은 직접 구현하려면 상당한 작업이 필요했을 흔한 문제를 해결해줍니다.
collections: 특수한 컨테이너 타입들:
from collections import Counter, defaultdict, deque
Counter(["a", "b", "a", "c", "a"]) # Counter({'a': 3, 'b': 1, 'c': 1})
defaultdict(list) # 없는 키를 자동으로 만들어주는 dict
deque([1, 2, 3], maxlen=5) # 양쪽 끝에서 빠르게 추가/제거itertools: 이터러블을 다루는 도구들:
import itertools
list(itertools.chain([1, 2], [3, 4])) # [1, 2, 3, 4]
list(itertools.islice(range(100), 5)) # [0, 1, 2, 3, 4]
list(itertools.combinations([1, 2, 3], 2)) # [(1, 2), (1, 3), (2, 3)]
list(itertools.product([0, 1], repeat=2)) # [(0,0), (0,1), (1,0), (1,1)]sys: 파이썬 인터프리터에 접근하기:
import sys
sys.argv # 명령줄 인자 리스트
sys.exit(1) # 상태 코드와 함께 종료
sys.version # 파이썬 버전 문자열서드파티 패키지: 표준 라이브러리를 넘어서, pip는 커뮤니티 패키지를 설치합니다:
pip install requests # HTTP 라이브러리
pip install pandas # 데이터 처리
pip install numpy # 수치 계산서드파티 패키지는 이 가이드의 범위 밖이지만, 패턴은 항상 동일합니다: pip install한 다음 import하세요.
실전 활용
random, string, datetime을 결합해서 타임스탬프가 붙은 고유한 게임 ID를 생성합니다:
import random
import string
from datetime import datetime
def generate_game_id(length: int = 8) -> str:
chars = string.ascii_uppercase + string.digits
return "".join(random.choices(chars, k=length))
def timestamp() -> str:
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
game_id = generate_game_id()
print(f"[{timestamp()}] Starting game {game_id}")
scores = [random.randint(50, 100) for _ in range(5)]
print(f"Round scores: {scores}")
print(f"Best: {max(scores)}")
