Python 기초와 설계 철학
Python은 왜 만들어졌는가
Guido van Rossum이 1989년 크리스마스 연휴에 시작해 1991년 공개한 Python은 ABC라는 교육용 언어의 후계자로 탄생했다. ABC의 읽기 쉬운 문법을 이어받으면서 시스템 콜·예외 처리·확장성을 더한 것이 초기 Python의 정체성이었다. 언어 이름은 영국 코미디 프로그램 Monty Python's Flying Circus에서 따왔다.
오늘날 Python은 거의 모든 분야에서 쓰인다. 데이터 엔지니어링(Airflow, Spark PySpark, dbt 매크로), 백엔드 웹(FastAPI, Django), AI/ML(PyTorch, TensorFlow), 인프라 자동화(Ansible), 스크립팅. Kubernetes나 Go 생태계처럼 단일 도메인의 표준이 아니라 여러 도메인에 걸쳐 "공용어"처럼 쓰인다는 점이 다른 언어와 다르다.
Zen of Python: 철학을 코드로 만들다
Python의 설계 원칙은 import this를 실행하면 나오는 19개의 잠언으로 요약된다. Tim Peters가 1999년에 작성하고 PEP 20으로 형식화된 이 문서는 "언어를 어떻게 써야 하는지"보다 "언어를 어떻게 설계해야 하는지" 의 원칙이다.
중요한 원칙 몇 가지를 실무 관점에서 풀어본다.
"Explicit is better than implicit" — 암묵적인 동작을 줄인다. from module import *보다 from module import func가 낫고, 매직 메서드가 어디서 호출되는지 독자가 예측할 수 있어야 한다.
"Readability counts" — Python은 코드를 쓰는 것보다 읽는 시간이 더 많다는 전제 아래 설계됐다. 변수명 축약, 한 줄 짜기 경쟁은 Python 철학에 반한다.
"Errors should never pass silently. Unless explicitly silenced." — except: pass는 Python 커뮤니티에서 코드 리뷰 때 가장 먼저 지적받는 패턴이다. 예외를 삼키는 것은 허용되지만 의식적인 결정이어야 한다.
"There should be one-- and preferably only one --obvious way to do it." — Go의 gofmt가 포맷을 강제하듯, Python은 PEP 8과 관례로 "한 가지 방법"을 유도한다. 같은 일을 하는 코드가 10가지 방식으로 쓰이면 팀 코드베이스가 흩어진다.
import this # Python 인터프리터에서 실행하면 전체 19개 잠언이 출력된다Python 버전 현황 (2026년 기준)
현재 주요 버전과 변화 포인트:
- Python 3.12 (2023.10): 12% 성능 향상, PEP 695 타입 파라미터 문법(
def f[T](x: T) -> T), 개선된 f-string 파서 - Python 3.13 (2024.10): 실험적 free-threading(PEP 703), JIT 컴파일러(PEP 744), 개선된 대화형 인터프리터
- Python 3.14 (2025.10): free-threading 안정화, GIL 제거 시 단일 스레드 성능 패널티 약 5~10%로 축소
Python 2는 2020년 EOL이 됐다. 현재 실무에서 Python 2를 새로 쓸 이유는 없다.
free-threading(GIL 제거)이란? CPython의 Global Interpreter Lock(GIL)은 한 번에 하나의 스레드만 Python 바이트코드를 실행하도록 하는 뮤텍스다. CPU-bound 멀티스레드 프로그램의 성능 병목이었지만, GIL 덕분에 메모리 안전성을 단순하게 보장할 수 있었다. Python 3.13부터 실험적으로 GIL 없이 빌드할 수 있다. 아직 대부분의 C 확장 라이브러리(numpy 포함)가 free-threading을 완전히 지원하지 않아 실무 도입은 2026~2027년이 분기점으로 보인다.
기본 문법: 들여쓰기가 구조다
Python에서 블록은 들여쓰기로 표현한다. 중괄호가 없다.
def greet(name: str) -> str:
if name:
return f"안녕하세요, {name}님"
return "안녕하세요"Go나 Java에서 넘어오면 처음에는 낯설지만, 들여쓰기 강제는 실제로 스타일 논쟁을 줄이고 팀 코드베이스의 가독성을 높인다. Python 공식 스타일 가이드 PEP 8은 스페이스 4칸을 권장한다. 탭과 스페이스를 섞으면 TabError가 발생한다.
동적 타이핑과 덕 타이핑
Python은 동적 타입 언어다. 변수는 타입을 선언하지 않고, 런타임에 객체에 타입이 결정된다.
x = 42 # x는 int 객체를 가리킨다
x = "hello" # 같은 이름이 str 객체를 가리키도록 바뀐다
x = [1, 2, 3] # list도 가능더 중요한 개념은 덕 타이핑(Duck Typing) 이다. "오리처럼 걷고 오리처럼 꽥꽥대면 오리다"라는 말에서 유래했다. 객체가 어떤 클래스의 인스턴스인지보다 어떤 메서드와 속성을 갖고 있는지를 기준으로 다형성을 구현한다.
def print_length(obj):
print(len(obj)) # __len__이 구현된 모든 객체를 받을 수 있다
print_length("hello") # 5
print_length([1, 2, 3]) # 3
print_length({"a": 1}) # 1인터페이스를 명시적으로 선언하지 않아도 다형성을 얻을 수 있다. Go의 암묵적 인터페이스(메서드 집합이 맞으면 구현)와 개념이 닮았지만, Python은 타입 체크 자체가 런타임에 이루어진다는 차이가 있다.
핵심 내장 타입
Python의 주요 타입을 불변(immutable)과 가변(mutable)으로 나눠 정리한다.
불변 타입:
n: int = 42
f: float = 3.14
b: bool = True # True/False는 int의 서브클래스 (True == 1)
s: str = "안녕" # 유니코드 문자열, 불변
t: tuple = (1, "a", 3.0) # 혼합 타입 허용, 불변
by: bytes = b"\x00\xff"
none_val = None # NoneType의 유일한 인스턴스가변 타입:
lst: list = [1, 2, 3]
dct: dict = {"key": "value"}
st: set = {1, 2, 3}불변 타입은 해시 가능(hashable)하므로 dict 키나 set 원소로 쓸 수 있다. 가변 타입은 해시가 불가하다.
d = {(1, 2): "tuple key"} # OK: tuple은 불변, 해시 가능
# d = {[1, 2]: "list key"} # TypeError: list는 해시 불가함수와 시그니처
Python 함수는 first-class object다. 변수에 할당하고, 인자로 전달하고, 반환값으로 내보낼 수 있다.
def add(x: int, y: int) -> int:
return x + y
# 기본값 파라미터
def greet(name: str, greeting: str = "안녕하세요") -> str:
return f"{greeting}, {name}님"
# *args: 위치 인자를 tuple로 수집 / **kwargs: 키워드 인자를 dict로 수집
def log(*args: str, level: str = "INFO", **kwargs: str) -> None:
print(f"[{level}]", *args, kwargs)* 뒤에 오는 파라미터는 반드시 키워드로만 전달해야 하는 keyword-only 파라미터가 된다.
def create_user(name: str, *, admin: bool = False) -> None:
...
create_user("Alice", admin=True) # OK
# create_user("Alice", True) # TypeError: admin은 keyword-only/ 앞의 파라미터는 위치 전용(positional-only, Python 3.8+)이다.
def distance(x: float, y: float, /) -> float:
return (x ** 2 + y ** 2) ** 0.5
distance(3.0, 4.0) # OK
# distance(x=3.0, y=4.0) # TypeError타입 힌트: 선택적이지만 점점 중요해지는 도구
Python 3.5(PEP 484)부터 타입 힌트가 공식화됐다. 런타임에는 강제되지 않지만, mypy·pyright 같은 정적 분석 도구로 검증할 수 있다.
Python 3.9(PEP 585) 이후에는 list[int], dict[str, int]처럼 built-in 타입에 직접 제네릭 문법을 쓸 수 있다. 이전에는 from typing import List, Dict를 임포트해야 했다.
# 3.9+ 권장: built-in 타입 직접 사용
def process(items: list[int]) -> dict[str, int]:
...
# 3.10+ union 문법 (PEP 604): int | str | None
def parse(value: str | int | None) -> float:
...
# 3.12+ 타입 파라미터 문법 (PEP 695)
def first[T](items: list[T]) -> T:
return items[0]타입 힌트는 Go의 정적 타입처럼 컴파일 타임 보장을 주지는 않는다. 하지만 코드를 읽는 사람과 IDE, 그리고 CI에서 돌리는 타입 체커에게 의도를 전달하는 가장 명확한 방식이다. 대규모 Python 코드베이스에서는 타입 힌트 없이 리팩터링하는 것이 훨씬 위험하다.
Go와의 비교: 언제 Python을, 언제 Go를
직전 시리즈에서 Go를 배운 독자를 위해 간단히 비교한다.
| 관점 | Go | Python |
|---|---|---|
| 타이핑 | 정적, 컴파일 타임 검증 | 동적, 런타임 + 선택적 정적 분석 |
| 성능 | 낮은 오버헤드, GC 지연 예측 가능 | 인터프리터, C 확장으로 핫 패스 가속 |
| 동시성 | goroutine + channel (CSP) | asyncio(이벤트 루프) + GIL (free-threading 실험 중) |
| 생태계 강점 | 인프라 도구, 네트워크 서비스 | 데이터/AI, 스크립팅, 빠른 프로토타이핑 |
| 배포 | 단일 바이너리 | 인터프리터 + 의존성 번들 필요 |
실무에서는 "어느 언어가 더 좋은가"보다 "이 작업에 어느 언어가 더 맞는가"를 판단한다. Kubernetes 오퍼레이터는 Go로, Airflow DAG는 Python으로, Kafka 소비자는 둘 다 가능하다.
CPython의 메모리 모델: 레퍼런스 카운팅
Python 객체는 레퍼런스 카운팅으로 관리된다. 변수가 객체를 가리키면 카운트가 올라가고, 범위를 벗어나면 내려간다. 카운트가 0이 되면 즉시 메모리가 해제된다. 순환 참조는 cyclic garbage collector가 별도로 처리한다.
import sys
a = [1, 2, 3]
b = a # 같은 객체를 가리키므로 카운트 2
print(sys.getrefcount(a)) # 3 (getrefcount 호출 자체도 카운트를 올림)
del b # 카운트 2로 감소
del a # 카운트 1 → 0, 메모리 해제Go의 GC가 백그라운드에서 주기적으로 돌면서 메모리를 회수하는 것과 달리, Python의 레퍼런스 카운팅은 대부분의 객체를 즉시 해제한다. 이 차이는 I/O 집약적 코드(파일, 소켓, DB 커넥션)에서 자원 해제 타이밍에 영향을 준다. Python에서 파일과 네트워크 자원은 with 문(컨텍스트 매니저)으로 명시적으로 닫아야 한다.
with open("data.csv") as f:
content = f.read()
# with 블록을 벗어나면 __exit__가 호출되어 파일이 닫힘정리
Python은 읽기 쉬운 코드와 범용성을 핵심 가치로 설계됐다. 동적 타이핑과 덕 타이핑이 유연성을 주고, 타입 힌트가 대규모 코드베이스의 안전망이 된다. Python 3.13/3.14에서 free-threading이 실험적으로 도입되면서 CPU-bound 병렬화 한계가 조금씩 완화되고 있다.
실무에서는 다음 순서로 익히면 좋다.
- PEP 8 스타일 가이드를 기준으로 읽기 쉬운 코드 작성 습관을 먼저 만든다.
- 내장 타입의 특성(가변/불변, 해시 가능성)을 정확히 이해한다.
- 타입 힌트를 일관되게 쓰고 CI에 mypy나 pyright를 붙인다.
- 제너레이터·comprehension·표준 라이브러리에 익숙해진다(다음 장 주제).
- 프레임워크를 쓰기 전에 그 프레임워크가 Python의 어느 부분을 활용하는지 이해한다.
References
- Python Documentation, "What's New In Python 3.13": https://docs.python.org/3/whatsnew/3.13.html
- Python Documentation, "What's New In Python 3.14": https://docs.python.org/3/whatsnew/3.14.html
- PEP 20 – The Zen of Python: https://peps.python.org/pep-0020/
- PEP 484 – Type Hints: https://peps.python.org/pep-0484/
- PEP 585 – Type Hinting Generics In Standard Collections: https://peps.python.org/pep-0585/
- PEP 703 – Making the Global Interpreter Lock Optional in CPython: https://peps.python.org/pep-0703/
- Real Python, "What Exactly Is the Zen of Python?": https://realpython.com/zen-of-python/
- Python Docs, "free-threading support": https://docs.python.org/3/howto/free-threading-python.html