Language
검색

Python 실무 팁: 문법 자랑이 아니라, 코드가 짧아지고 빨라지는 것만

위에서 내려다본 나선 계단의 곡선 사진

·

조회수 7
파이썬 코드를 빠르게 만드는 가장 큰 요인은 무엇인가?
문법 요령이 아니라 자료구조 선택이다. 리스트에서 값을 찾는 코드를 집합이나 딕셔너리로 바꾸면 데이터가 커질수록 차이가 벌어진다. 반복문 안에서 매번 리스트를 뒤지는 코드 한 줄이, 나머지 모든 미세 최적화를 합친 것보다 크게 느리다.

파이썬 팁 모음은 대체로 “이렇게 하면 한 줄로 됩니다”에서 끝난다. 한 줄로 줄었지만 다음 사람이 못 읽는 코드가 되는 경우도 많다.

이 글의 기준은 두 가지다. 읽기가 나빠지지 않으면서 짧아지는 것, 그리고 데이터가 커질 때 실제로 빨라지는 것. 이 둘에 해당하지 않으면 뺐다.

자료구조를 바꿔서 해결되는 것 (5개)

1. 포함 검사는 집합으로

# 전: 리스트를 매번 처음부터 훑는다
valid_ids = [1, 5, 9, 12, ...]        # 1만 개
for row in rows:                       # 10만 행
    if row.id in valid_ids: ...

# 후
valid_ids = {1, 5, 9, 12, ...}
for row in rows:
    if row.id in valid_ids: ...

리스트의 in은 원소를 하나씩 비교하고, 집합은 해시로 한 번에 찾는다. 데이터가 커질수록 차이가 벌어진다. 이 글에서 가장 효과가 큰 항목이다.

2. 짝 맞추기는 딕셔너리로 — 두 목록을 겹쳐 찾는 이중 반복문은 대개 딕셔너리 하나로 바뀐다.

# 전
for order in orders:
    for user in users:
        if user.id == order.user_id: ...

# 후
user_by_id = {u.id: u for u in users}
for order in orders:
    user = user_by_id.get(order.user_id)

3. 개수 세기는 Counter

from collections import Counter
counts = Counter(words)
counts.most_common(10)

4. 없는 키 처리에는 defaultdict

from collections import defaultdict
groups = defaultdict(list)
for item in items:
    groups[item.category].append(item)   # 키 존재 확인 불필요

5. 값만 담는 클래스는 dataclass

from dataclasses import dataclass

@dataclass
class User:
    id: int
    name: str
    email: str = ""

__init__, __repr__, __eq__가 자동으로 생긴다.

반복문을 줄이는 것 (5개)

6. 컴프리헨션 — 만들어서 추가하는 패턴을 한 줄로.

names = [u.name for u in users if u.active]

7. enumerate — 인덱스가 필요할 때 카운터 변수를 만들지 않는다.

for i, line in enumerate(lines, start=1): ...

8. zip — 여러 목록을 나란히 돌 때.

for name, score in zip(names, scores): ...

9. any / all — 조건 검사 반복문을 한 줄로. 게다가 조건을 만족하면 즉시 멈춘다.

if any(u.is_admin for u in users): ...

10. dict.getsetdefault — 키 존재 확인 분기를 없앤다.

가독성이 나빠지는 선도 분명히 해두자. 컴프리헨션에 조건이 두 개 이상 붙고 중첩이 들어가면 그때부터는 일반 반복문이 낫다. 한 줄에 들어간다고 좋은 게 아니라, 한 번 읽고 이해되면 좋은 것이다.

파일·경로·문자열 (5개)

11. pathlib — 문자열로 경로를 조립하지 않는다.

from pathlib import Path
p = Path("data") / "2026" / "log.txt"
p.write_text("hello", encoding="utf-8")

12. 인코딩을 항상 명시한다 — 한글이 깨지는 사고의 대부분이 여기서 온다. 운영체제마다 기본 인코딩이 달라서, 내 컴퓨터에서는 되고 서버에서는 깨진다.

open("f.csv", encoding="utf-8")          # 항상 붙인다
open("f.csv", encoding="utf-8-sig")      # 엑셀이 만든 CSV의 BOM 처리

13. with 구문 — 파일은 반드시 이 형태로 연다. 예외가 나도 닫힌다.

14. f-string — 포맷 지정도 가능하다.

f"{value:,.2f}"      # 1,234.57
f"{name:<10}"        # 왼쪽 정렬
f"{obj=}"            # obj=<값>  (디버깅용)

15. 문자열 연결은 join

# 전: 반복마다 새 문자열이 만들어진다
s = ""
for x in items: s += str(x)

# 후
s = "".join(str(x) for x in items)

속도가 실제로 달라지는 것 (5개)

16. 제너레이터로 메모리 아끼기 — 전부 메모리에 올릴 필요가 없을 때.

# 전: 파일 전체를 메모리에
lines = open("big.log", encoding="utf-8").readlines()

# 후: 한 줄씩 흘려보낸다
with open("big.log", encoding="utf-8") as f:
    for line in f: ...

17. 반복문 밖으로 빼기 — 루프 안에서 매번 계산되는 것을 밖으로.

# 전
for x in items:
    if x in set(valid):   # 매 반복마다 집합을 새로 만든다
        ...
# 후
valid_set = set(valid)
for x in items:
    if x in valid_set: ...

18. 내장 함수와 표준 라이브러리 우선sum, max, sorted, itertools는 C로 구현돼 있어 같은 일을 파이썬 반복문으로 짜는 것보다 빠르다.

19. functools.lru_cache — 같은 인자로 반복 호출되는 순수 함수에.

from functools import lru_cache

@lru_cache(maxsize=None)
def expensive(n): ...

20. 측정하고 고친다 — 이게 마지막이자 가장 중요한 항목이다.

import cProfile
cProfile.run("main()")

느리다고 짐작한 곳과 실제로 느린 곳은 대개 다르다. 프로파일러를 한 번 돌리는 것이 최적화 팁 열 개보다 낫다.

하지 말아야 할 최적화

  • 가독성을 버리는 한 줄. 중첩 컴프리헨션, 과한 람다, 삼항 연산자 중첩은 짧지만 비싸다
  • 조기 최적화. 실행 시간의 대부분은 코드의 극히 일부에서 발생한다. 측정 없이 전부를 튜닝하는 것은 낭비다
  • 파이썬으로 안 되는 것을 파이썬으로 짜기. 대규모 수치 연산은 NumPy 같은 라이브러리에 맡긴다. 순수 파이썬 반복문으로는 자릿수 차이를 못 따라잡는다

자주 묻는 질문

어떤 버전 기준인가요?

이 글의 항목은 파이썬 3.8 이상이면 대부분 그대로 쓸 수 있습니다. dataclass는 3.7부터, f-string의 f"{obj=}" 표기는 3.8부터입니다. 새 프로젝트라면 현재 지원 중인 최신 안정 버전을 쓰는 것이 성능과 보안 양쪽에서 유리합니다.

속도가 정말 그렇게 차이 나나요?

항목마다 다릅니다. 자료구조 교체는 데이터가 커질수록 차이가 커지는 구조적 개선이고, 문자열 join이나 반복문 밖으로 빼기는 반복 횟수에 비례합니다. 반면 나머지 항목은 가독성 개선이 주된 이득입니다. 자기 코드에서 실제로 얼마나 차이 나는지는 timeit이나 cProfile로 직접 재보세요.

초보자가 바로 써도 되나요?

순서를 권합니다. 먼저 자료구조(1~5번)와 파일·인코딩(11~15번)을 익히세요. 버그를 줄이고 즉시 도움이 됩니다. 컴프리헨션과 제너레이터는 기본 반복문이 익숙해진 뒤에 넘어가는 편이 좋습니다. 원리를 모른 채 쓰면 디버깅이 어려워집니다.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *