본문으로 건너뛰기
Advertisement

표준 라이브러리 핵심 — os, sys, re, datetime, collections, itertools

Python의 **표준 라이브러리(stdlib)**는 추가 설치 없이 바로 사용할 수 있는 강력한 모듈 집합입니다. 실무에서 가장 많이 사용하는 핵심 모듈을 완전 정복합니다.


os — 운영체제 인터페이스

파일 시스템, 환경 변수, 프로세스 관리에 사용합니다.

import os

# 환경 변수
db_url = os.environ.get("DATABASE_URL", "sqlite:///dev.db")
os.environ["MY_VAR"] = "value"

# 현재 디렉터리
print(os.getcwd())
os.chdir("/tmp")

# 경로 조작
path = os.path.join("data", "2024", "report.csv")
print(os.path.exists(path))
print(os.path.dirname(path)) # data/2024
print(os.path.basename(path)) # report.csv
print(os.path.splitext(path)) # ('data/2024/report', '.csv')

# 파일/디렉터리 생성 및 삭제
os.makedirs("logs/2024/03", exist_ok=True)
os.remove("old_file.txt")
os.rmdir("empty_dir")

# 디렉터리 내용 순회
for dirpath, dirnames, filenames in os.walk("project/"):
for fname in filenames:
full_path = os.path.join(dirpath, fname)
print(full_path)
# 프로세스 정보
print(os.getpid()) # 현재 프로세스 ID
print(os.cpu_count()) # CPU 코어 수

# 명령 실행 (subprocess 사용 권장)
exit_code = os.system("ls -la")

sys — 인터프리터 정보

Python 인터프리터 자체에 접근하는 모듈입니다.

import sys

# Python 버전
print(sys.version) # '3.12.0 (main, ...'
print(sys.version_info) # sys.version_info(major=3, minor=12, ...)

# 플랫폼
print(sys.platform) # 'linux', 'darwin', 'win32'

# 커맨드라인 인수
# python script.py arg1 arg2 --flag
print(sys.argv) # ['script.py', 'arg1', 'arg2', '--flag']

# 표준 스트림
sys.stdout.write("표준 출력\n")
sys.stderr.write("오류 출력\n")

# 모듈 검색 경로
sys.path.insert(0, "/custom/lib")

# 최대 재귀 깊이
print(sys.getrecursionlimit()) # 1000 (기본값)
sys.setrecursionlimit(5000)

# 인터프리터 종료
# sys.exit(0) # 정상 종료
# sys.exit(1) # 오류 종료

# 객체 크기 (바이트)
print(sys.getsizeof([1, 2, 3])) # 80
print(sys.getsizeof("hello")) # 54
# 버전 체크 패턴
if sys.version_info < (3, 12):
raise RuntimeError("Python 3.12 이상이 필요합니다.")

re — 정규표현식 완전 정복

import re

text = "2024년 3월 15일, 연락처: 010-1234-5678, 이메일: user@example.com"

# 기본 함수들
# re.search(): 첫 번째 매치 찾기
match = re.search(r"\d{4}년", text)
if match:
print(match.group()) # 2024년
print(match.start()) # 0
print(match.end()) # 6

# re.findall(): 모든 매치를 리스트로
phones = re.findall(r"\d{3}-\d{4}-\d{4}", text)
print(phones) # ['010-1234-5678']

# re.finditer(): 모든 매치를 이터레이터로
for m in re.finditer(r"\d+", text):
print(f"{m.group()} at {m.start()}")

# re.sub(): 치환
cleaned = re.sub(r"\d{3}-\d{4}-\d{4}", "***-****-****", text)

# re.split(): 분리
parts = re.split(r"[,\s]+", "apple, banana cherry")
print(parts) # ['apple', 'banana', 'cherry']
# 그룹 캡처
date_pattern = re.compile(r"(\d{4})년\s*(\d{1,2})월\s*(\d{1,2})일")
m = date_pattern.search(text)
if m:
year, month, day = m.groups()
print(f"{year}-{month:>02}-{day:>02}")

# 이름 있는 그룹
email_pattern = re.compile(
r"(?P<user>[\w.]+)@(?P<domain>[\w.]+)\.(?P<tld>[a-z]{2,})"
)
m = email_pattern.search(text)
if m:
print(m.group("user")) # user
print(m.group("domain")) # example
print(m.groupdict()) # {'user': 'user', 'domain': 'example', 'tld': 'com'}
# 컴파일된 패턴으로 성능 향상
EMAIL_RE = re.compile(
r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
re.IGNORECASE
)

def extract_emails(text: str) -> list[str]:
return EMAIL_RE.findall(text)

# re.VERBOSE로 가독성 높은 패턴
url_pattern = re.compile(r"""
https?:// # 스킴
(?:www\.)? # 선택적 www
([\w-]+) # 도메인
\.([a-z]{2,}) # TLD
(/[\w./%-]*)? # 경로 (선택)
""", re.VERBOSE)

datetime — 날짜와 시간 처리

from datetime import date, time, datetime, timedelta, timezone
import datetime as dt

# date: 날짜만
today = date.today()
print(today) # 2024-03-15
print(today.year, today.month, today.day)
birthday = date(1990, 5, 20)
print((today - birthday).days) # 나이를 일수로

# time: 시간만
t = time(14, 30, 0)
print(t.isoformat()) # '14:30:00'

# datetime: 날짜 + 시간
now = datetime.now()
utc_now = datetime.now(timezone.utc)

print(now.isoformat()) # '2024-03-15T14:30:00.123456'
print(now.strftime("%Y년 %m월 %d일 %H:%M")) # '2024년 03월 15일 14:30'

# 문자열 파싱
dt_obj = datetime.strptime("2024-03-15 14:30", "%Y-%m-%d %H:%M")

# timedelta: 기간
delta = timedelta(days=30, hours=2, minutes=15)
future = now + delta
past = now - timedelta(weeks=2)

print(f"30일 후: {future.date()}")
# 타임존 처리 (Python 3.9+는 zoneinfo 사용 권장)
from zoneinfo import ZoneInfo

seoul = ZoneInfo("Asia/Seoul")
ny = ZoneInfo("America/New_York")

now_seoul = datetime.now(seoul)
now_ny = now_seoul.astimezone(ny)
print(f"서울: {now_seoul.strftime('%H:%M %Z')}")
print(f"뉴욕: {now_ny.strftime('%H:%M %Z')}")

# ISO 형식 파싱 (Python 3.7+)
dt_obj = datetime.fromisoformat("2024-03-15T14:30:00+09:00")

collections — 고급 컨테이너

Counter — 개수 세기

from collections import Counter

words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
c = Counter(words)
print(c) # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
print(c.most_common(2)) # [('apple', 3), ('banana', 2)]
print(c["apple"]) # 3

# 문자 빈도
char_count = Counter("mississippi")
print(char_count) # Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})

# Counter 연산
c1 = Counter(a=3, b=2)
c2 = Counter(a=1, b=4, c=1)
print(c1 + c2) # Counter({'b': 6, 'a': 4, 'c': 1})
print(c1 - c2) # Counter({'a': 2})

defaultdict — 기본값이 있는 dict

from collections import defaultdict

# 기본값 없는 dict는 KeyError 발생
# regular_dict = {}
# regular_dict["missing"] # KeyError!

# defaultdict는 기본값 자동 생성
word_positions = defaultdict(list)
text = "the quick brown fox jumps over the lazy dog"
for i, word in enumerate(text.split()):
word_positions[word].append(i)

print(word_positions["the"]) # [0, 6]

# 중첩 defaultdict
nested = defaultdict(lambda: defaultdict(int))
nested["group1"]["count"] += 1

OrderedDict

from collections import OrderedDict

# Python 3.7+에서 일반 dict도 순서 보장
# OrderedDict의 특수 기능: move_to_end
od = OrderedDict([("a", 1), ("b", 2), ("c", 3)])
od.move_to_end("a") # 맨 뒤로
od.move_to_end("c", last=False) # 맨 앞으로
print(list(od.keys())) # ['c', 'b', 'a']

# LRU 캐시 구현에 활용

deque — 양방향 큐

from collections import deque

d = deque([1, 2, 3], maxlen=5)
d.appendleft(0) # 왼쪽에 추가
d.append(4) # 오른쪽에 추가
d.popleft() # 왼쪽에서 제거 — O(1)!
d.pop() # 오른쪽에서 제거

# 슬라이딩 윈도우 패턴
def sliding_window_avg(data: list[float], size: int) -> list[float]:
window = deque(maxlen=size)
result = []
for val in data:
window.append(val)
if len(window) == size:
result.append(sum(window) / size)
return result

namedtuple — 이름 있는 튜플

from collections import namedtuple
from typing import NamedTuple

# 구식 방법
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y) # 3 4
print(p._asdict()) # {'x': 3, 'y': 4}

# 현대적 방법 (타입 힌트 포함)
class Coordinate(NamedTuple):
latitude: float
longitude: float
altitude: float = 0.0

coord = Coordinate(37.5665, 126.9780)
print(coord.latitude) # 37.5665
lat, lon, alt = coord # 언패킹

itertools — 이터레이터 도구

import itertools

# 무한 이터레이터
for i, val in enumerate(itertools.count(10, 2)):
print(val) # 10, 12, 14, ...
if i >= 4:
break

colors = itertools.cycle(["빨강", "초록", "파랑"])
for _ in range(6):
print(next(colors)) # 빨강, 초록, 파랑, 빨강, ...

# chain: 여러 이터러블 연결
result = list(itertools.chain([1, 2], [3, 4], [5, 6]))
print(result) # [1, 2, 3, 4, 5, 6]

# islice: 이터레이터 슬라이싱
first_5 = list(itertools.islice(itertools.count(), 5))
print(first_5) # [0, 1, 2, 3, 4]

# product: 데카르트 곱
for combo in itertools.product("AB", repeat=2):
print(combo) # ('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B')

# combinations, permutations
print(list(itertools.combinations([1, 2, 3], 2)))
# [(1, 2), (1, 3), (2, 3)]

print(list(itertools.permutations([1, 2, 3], 2)))
# [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

# groupby: 연속된 키로 그룹화
data = [("A", 1), ("A", 2), ("B", 3), ("B", 4), ("A", 5)]
for key, group in itertools.groupby(data, key=lambda x: x[0]):
print(key, list(group))
# A [('A', 1), ('A', 2)]
# B [('B', 3), ('B', 4)]
# A [('A', 5)]

실전 예제: 로그 분석기

import re
import sys
from collections import Counter, defaultdict
from datetime import datetime
from pathlib import Path
from itertools import groupby

LOG_PATTERN = re.compile(
r"(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) "
r"(?P<level>INFO|WARNING|ERROR|DEBUG) "
r"(?P<message>.+)"
)

def parse_log_line(line: str) -> dict | None:
m = LOG_PATTERN.match(line)
if not m:
return None
return {
"timestamp": datetime.strptime(m.group("timestamp"), "%Y-%m-%d %H:%M:%S"),
"level": m.group("level"),
"message": m.group("message"),
}

def analyze_logs(log_file: str) -> None:
path = Path(log_file)
if not path.exists():
print(f"파일 없음: {log_file}", file=sys.stderr)
sys.exit(1)

entries = []
with open(path, encoding="utf-8") as f:
for line in f:
entry = parse_log_line(line.strip())
if entry:
entries.append(entry)

# 레벨별 카운트
level_counts = Counter(e["level"] for e in entries)
print("=== 레벨별 로그 수 ===")
for level, count in level_counts.most_common():
print(f" {level}: {count}")

# 시간별 오류 집계
error_entries = [e for e in entries if e["level"] == "ERROR"]
hour_errors = defaultdict(int)
for entry in error_entries:
hour_errors[entry["timestamp"].hour] += 1

print("\n=== 시간별 오류 수 ===")
for hour in sorted(hour_errors):
print(f" {hour:02d}시: {hour_errors[hour]}")

if __name__ == "__main__":
analyze_logs(sys.argv[1] if len(sys.argv) > 1 else "app.log")

고수 팁

팁 1: os.scandir()os.listdir()보다 빠름

import os

# 느린 방법
files = [f for f in os.listdir(".") if os.path.isfile(f)]

# 빠른 방법 (stat 정보 캐싱)
files = [e.name for e in os.scandir(".") if e.is_file()]

팁 2: Counter 업데이트

from collections import Counter

total = Counter()
for batch in data_batches:
total.update(Counter(batch))

팁 3: itertools.chain.from_iterable로 중첩 리스트 평탄화

import itertools

nested = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
flat = list(itertools.chain.from_iterable(nested))
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

팁 4: datetime.timezone.utc vs pytz

from datetime import datetime, timezone

# Python 3.9+ — 추가 패키지 불필요
from zoneinfo import ZoneInfo
now_utc = datetime.now(timezone.utc)
now_kst = now_utc.astimezone(ZoneInfo("Asia/Seoul"))
Advertisement