Split View: Python 완전 가이드 — FastAPI·AsyncIO·Pydantic·uv·Polars·AI 엔지니어링 (Season 2 Ep 5, 2025)
Python 완전 가이드 — FastAPI·AsyncIO·Pydantic·uv·Polars·AI 엔지니어링 (Season 2 Ep 5, 2025)
들어가며 — 2025년의 Python은 2015년의 Python이 아니다
Python을 10년 전 배우고 지금 돌아온 사람이 가장 놀랄 변화들:
- uv (2024): pip·poetry·pyenv·pip-tools를 모두 대체하는 Rust 작성 통합 도구. 10~100배 빠름
- Pydantic v2 (2023): 검증 엔진을 Rust로 재작성. 5~50배 빠름
- FastAPI: 타입 힌트 기반 API 프레임워크의 사실상 표준
- Polars (2024~): Pandas 대체. Rust 기반, 10~100배 빠름, 메모리 효율
- Python 3.13 (2024/10): Free-threading(No-GIL) 실험, JIT 컴파일러, 개선된 인터랙티브 쉘
- LangChain·LangGraph·Pydantic AI (2024~): AI 에이전트 스택
- Ruff + mypy / pyright: Lint·Format·타입체크 모두 빠른 도구로 교체
Python은 "AI 엔지니어링의 공용어"로 확고히 자리잡았고, 도구 체인 전체가 Rust로 재작성되며 속도 문제도 해결되고 있다.
1부 — Python 3.13: 언어의 미래
1.1 Free-threading (No-GIL) — PEP 703
30년 된 GIL(Global Interpreter Lock)을 제거하는 Experimental Build 등장 (2024/10):
python3.13t # free-threaded build
- GIL: 한 번에 1개 스레드만 Python 바이트코드 실행
- No-GIL: 진정한 멀티코어 병렬성
한계: 현재 실험적, 단일 스레드 성능 10% 하락, C 확장 호환성 이슈. 20262027년 안정화 예상.
1.2 JIT Compiler — PEP 744
Copy-and-Patch JIT 추가 (실험):
python --enable-experimental-jit
초기 벤치마크는 미미(~5%)지만 장기적으로 PyPy 수준의 성능 가능성.
1.3 개선된 REPL
3.13의 표준 REPL이 드디어 사용 가능:
- 멀티라인 편집
- 구문 하이라이팅
- 페이스트 모드
- 블록 히스토리
PyPy·ipython에 의존했던 것들이 기본으로.
1.4 Type System 개선 (3.12~3.13)
# PEP 695: Generic Syntax
def first[T](items: list[T]) -> T:
return items[0]
class Stack[T]:
def __init__(self) -> None:
self.items: list[T] = []
# PEP 696: Type Parameter Defaults
def process[T = str](x: T) -> T: ...
# PEP 742: TypeIs
from typing import TypeIs
def is_str(x: object) -> TypeIs[str]:
return isinstance(x, str)
2부 — uv: 패키지 매니저 혁명
2.1 왜 uv가 게임 체인저인가
Astral이 만든 Rust 기반 통합 도구 (Ruff 제작사). 대체 대상:
- pip → 설치
- pip-tools → 해시 pinned requirements
- pyenv → 파이썬 버전 관리
- virtualenv/venv → 가상환경
- poetry → 프로젝트 관리
- pipx → CLI 도구 설치
속도: pip install이 10초 걸리는 프로젝트가 uv로는 0.1초.
2.2 기본 사용법
# 설치
curl -LsSf https://astral.sh/uv/install.sh | sh
# 프로젝트 초기화
uv init my-app
cd my-app
# 의존성 추가 (pyproject.toml 자동 업데이트)
uv add fastapi uvicorn
uv add --dev pytest ruff mypy
# 실행
uv run python main.py
uv run pytest
# Python 버전 관리
uv python install 3.13
uv python pin 3.13
# Lock 파일 (uv.lock)
uv lock
uv sync
2.3 uv script (단일 파일 Python)
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx",
# "rich",
# ]
# ///
import httpx
from rich import print
print(httpx.get("https://api.github.com").json())
uv run script.py
# 의존성 자동 다운로드 후 실행
2025년 새 프로젝트는 uv로 시작하는 것이 표준.
3부 — Pydantic v2: 타입 기반 검증
3.1 v2의 혁명
- Rust로 재작성된
pydantic-core - 5~50배 빠름
Annotated기반 강력한 제약- Serialization API 분리
3.2 기본 사용
from pydantic import BaseModel, EmailStr, Field
from typing import Annotated
from datetime import datetime
class User(BaseModel):
id: int
email: EmailStr
name: Annotated[str, Field(min_length=1, max_length=100)]
age: Annotated[int, Field(ge=0, le=150)]
created_at: datetime
# 검증 + 변환
user = User.model_validate({
"id": 1,
"email": "alice@example.com",
"name": "Alice",
"age": 30,
"created_at": "2025-01-01T00:00:00Z"
})
# 직렬화
json_str = user.model_dump_json()
dict_data = user.model_dump()
3.3 고급 기능
from pydantic import field_validator, model_validator
class Post(BaseModel):
title: str
content: str
tags: list[str] = []
@field_validator('title')
@classmethod
def title_not_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError('title cannot be empty')
return v.strip()
@model_validator(mode='after')
def check_tags_limit(self) -> 'Post':
if len(self.tags) > 10:
raise ValueError('too many tags')
return self
3.4 Pydantic Settings
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
api_key: str
debug: bool = False
class Config:
env_file = ".env"
settings = Settings()
환경변수·config 관리의 표준.
4부 — FastAPI: 타입 안전 API 표준
4.1 기본 예제
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
app = FastAPI()
class UserCreate(BaseModel):
email: str
password: str
class UserResponse(BaseModel):
id: int
email: str
@app.post("/users", response_model=UserResponse)
async def create_user(user: UserCreate) -> UserResponse:
# user는 이미 검증됨
# 응답은 UserResponse로 자동 직렬화
return UserResponse(id=1, email=user.email)
- Pydantic 모델이 요청·응답 스키마
- OpenAPI 자동 생성 (
/docs) - Dependency Injection 내장
4.2 Dependency Injection
async def get_db() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
yield session
async def get_current_user(token: str = Header(...)) -> User:
# 토큰 검증 로직
return user
@app.get("/me")
async def me(user: User = Depends(get_current_user)) -> UserResponse:
return user
4.3 2025년 FastAPI 스택
| 목적 | 라이브러리 |
|---|---|
| Web | FastAPI |
| ORM | SQLAlchemy 2.0 (async), SQLModel |
| DB 드라이버 | asyncpg (Postgres), motor (Mongo) |
| Migration | Alembic |
| 큐 | Dramatiq, Celery 5.x, Taskiq |
| 캐시 | Redis + aioredis |
| 테스트 | pytest + httpx |
| 관측성 | OpenTelemetry + Prometheus |
5부 — AsyncIO 실전
5.1 기본 구조
import asyncio
async def fetch(url: str) -> str:
# 시뮬레이션
await asyncio.sleep(1)
return f"data from {url}"
async def main() -> None:
# 순차
r1 = await fetch("a")
r2 = await fetch("b")
# 동시 (2배 빠름)
r1, r2 = await asyncio.gather(fetch("a"), fetch("b"))
asyncio.run(main())
5.2 TaskGroup (Python 3.11+)
async def main():
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(fetch("a"))
t2 = tg.create_task(fetch("b"))
t3 = tg.create_task(fetch("c"))
# 컨텍스트 나오면 모든 task 완료
# 하나라도 예외면 나머지 취소 + ExceptionGroup
print(t1.result(), t2.result(), t3.result())
TaskGroup이 asyncio.gather보다 낫다: 구조적 동시성, 자동 취소, 명확한 에러.
5.3 asyncio.timeout (Python 3.11+)
async def fetch_with_timeout():
async with asyncio.timeout(5.0):
return await slow_operation()
5.4 AsyncIO 함정 5가지
- 블로킹 코드 혼합:
requests.get대신httpx.AsyncClient asyncio.run중첩: 이벤트 루프 충돌.nest_asyncio는 아티 패턴- task 참조 유지 실패:
asyncio.create_task결과를 어딘가 저장해야 GC 안됨 - CPU 바운드 작업:
run_in_executor로 별도 스레드/프로세스 async def안에서time.sleep:asyncio.sleep써야
6부 — Polars: Pandas의 2025년 대체재
6.1 왜 Polars인가
| 항목 | Pandas | Polars |
|---|---|---|
| 엔진 | NumPy (C) | Rust + Arrow |
| 속도 | 기준 | 5~100배 빠름 |
| 메모리 | 많이 씀 | Pandas의 ~50% |
| 병렬성 | 거의 없음 | 자동 다중 스레드 |
| Lazy 실행 | ❌ | ✅ (옵티마이저) |
| 대규모 데이터 | 어려움 | 스트리밍 가능 |
6.2 기본 사용
import polars as pl
df = pl.read_csv("data.csv")
result = (
df
.filter(pl.col("age") > 30)
.group_by("country")
.agg([
pl.col("salary").mean().alias("avg_salary"),
pl.col("id").count().alias("count"),
])
.sort("avg_salary", descending=True)
)
6.3 Lazy Evaluation
lazy_df = pl.scan_csv("huge.csv") # 실제 읽지 않음
result = (
lazy_df
.filter(pl.col("year") == 2024)
.group_by("category")
.agg(pl.col("amount").sum())
.collect() # 이제 실행 (옵티마이저가 계획 수립)
)
쿼리 옵티마이저가 SQL DBMS처럼 실행 계획을 최적화.
6.4 2025년 전환 가이드
- 새 프로젝트: Polars 기본값
- 기존 Pandas 프로젝트: 점진 전환 (
df.to_pandas()브릿지) - 시각화는 여전히 Pandas가 편할 수 있음 (matplotlib/seaborn)
- ML 라이브러리 호환은 아직 Pandas 우세 (개선 중)
7부 — AI 엔지니어링 스택 2025
7.1 LangChain의 현재
LangChain은 2023년 폭발, 2024년 성장통, 2025년 LangGraph 중심으로 재편.
- LangChain Core: 기본 추상화 (Runnable)
- LangGraph: 상태 기반 에이전트 그래프 (2025년 표준)
- LangSmith: 관측성·평가
7.2 LangGraph 예제
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
class State(TypedDict):
messages: Annotated[list, operator.add]
def agent(state: State):
response = llm.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state: State) -> str:
last = state["messages"][-1]
if last.tool_calls:
return "tools"
return END
workflow = StateGraph(State)
workflow.add_node("agent", agent)
workflow.add_node("tools", tool_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("tools", "agent")
app = workflow.compile()
Directed Graph + 상태 머신 모델. 복잡한 에이전트 흐름에 이상적.
7.3 Pydantic AI (2024~)
Pydantic 팀이 만든 타입 안전 에이전트 프레임워크. 경량·직관적.
from pydantic_ai import Agent
from pydantic import BaseModel
class WeatherResponse(BaseModel):
city: str
temp_c: float
condition: str
agent = Agent(
'openai:gpt-4o',
result_type=WeatherResponse,
system_prompt='You are a weather assistant.',
)
result = await agent.run('Weather in Seoul?')
print(result.data) # WeatherResponse 타입
7.4 2025년 AI 스택 추천
| 상황 | 추천 |
|---|---|
| 복잡한 멀티스텝 에이전트 | LangGraph |
| 간단한 타입 안전 에이전트 | Pydantic AI |
| RAG 특화 | LlamaIndex |
| 프로덕션 최소 의존성 | OpenAI SDK 직접 |
| TS 팀과 협업 | Vercel AI SDK (TS) 매치 |
8부 — Modern Python 개발 환경
8.1 도구 체인 (2025 표준)
# pyproject.toml
[project]
name = "my-app"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.115",
"pydantic>=2.9",
"uvicorn>=0.30",
]
[dependency-groups]
dev = [
"pytest>=8",
"ruff>=0.7",
"mypy>=1.13",
]
[tool.ruff]
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B", "S", "C4", "PL"]
[tool.mypy]
strict = true
8.2 Ruff — 10~100배 빠른 린터/포매터
- Flake8, Black, isort, pyupgrade, pylint 대체
- Rust 작성
- 800+ 규칙 내장
8.3 타입 체커 선택
| 도구 | 특징 |
|---|---|
| mypy | 가장 오래됨, 느림, 보수적 |
| pyright | MS 제작, 빠름, 엄격 |
| pyre | Meta 제작, 서버 모드 |
| ty | Astral 제작 (2025), Rust 기반 (개발 중) |
2025년 추천: 신규는 pyright, 기존은 mypy 유지. ty 출시 기다리는 중.
8.4 테스트
# pytest + httpx for FastAPI
import pytest
from httpx import AsyncClient, ASGITransport
from myapp.main import app
@pytest.mark.asyncio
async def test_create_user():
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
r = await ac.post("/users", json={"email": "a@b.com", "password": "pw"})
assert r.status_code == 200
9부 — Python이 빛나는 분야 vs 빛나지 않는 분야
9.1 빛남
- AI/ML: PyTorch, JAX, scikit-learn, Hugging Face
- 데이터 엔지니어링: Airflow, dbt, Dagster
- API 서버: FastAPI의 생산성
- 과학 컴퓨팅: NumPy, SciPy
- 스크립팅/자동화: 글루 언어
- 교육: 학습 곡선 낮음
9.2 빛나지 않음
- CPU 바운드 고성능: Rust/Go/C++
- 모바일 앱: Swift/Kotlin
- 임베디드: C/Rust
- 게임 엔진: C++
- 브라우저: JS/TS (Pyodide는 특수 용도)
2025년 현실: Python은 "AI·데이터·API"의 표준이지만, "모든 것을 위한" 언어는 아니다.
10부 — Python 마스터 로드맵 6개월
Month 1: 3.13 기본기
- 타입 힌트 완전 이해 (PEP 695, Union, Literal, Protocol)
- uv로 프로젝트 관리
- Ruff + mypy/pyright 설정
Month 2: Pydantic + FastAPI
- Pydantic v2 심화 (Annotated, Validators)
- FastAPI 실전 API 만들기
- Dependency Injection 패턴
Month 3: AsyncIO
- TaskGroup, asyncio.timeout
- httpx·asyncpg 비동기 DB
- 동시성 제한 (Semaphore)
Month 4: 데이터 처리
- Polars 마스터
- SQL + SQLAlchemy 2.0 async
- DuckDB + Parquet
Month 5: AI 엔지니어링
- OpenAI/Anthropic SDK
- LangGraph 또는 Pydantic AI
- Vector DB (Qdrant, Pinecone, pgvector)
- RAG 파이프라인 구축
Month 6: 운영
- Docker + Kubernetes 배포
- 관측성 (OpenTelemetry, Prometheus)
- CI/CD (GitHub Actions)
- 프로파일링 (py-spy, scalene)
11부 — Python 체크리스트 12
- uv의 4가지 대체 도구(pip/poetry/pyenv/pip-tools)를 안다
- Pydantic v1 vs v2 주요 변화를 안다
- FastAPI Dependency Injection 사용법을 안다
- TaskGroup vs asyncio.gather 차이를 안다
- Polars Lazy Evaluation의 장점을 안다
- 타입 힌트 Protocol, TypeIs 사용법을 안다
- SQLAlchemy 2.0 async 스타일을 안다
- GIL과 Free-threading의 의미를 안다
- LangGraph와 LangChain의 관계를 안다
- Ruff가 대체하는 도구들을 안다
- Pydantic Settings로 설정 관리를 할 수 있다
- pytest + httpx로 async API 테스트를 쓸 수 있다
12부 — Python 안티패턴 10
pip install+requirements.txt만 사용: 재현성 취약.uv+uv.lock- 타입 힌트 없이 Python 코드 작성: mypy/pyright로 잡힐 버그 방치
from X import *: 네임스페이스 오염print()로 디버깅:logging또는richexcept Exception: pass: 조용히 실패. 구체 예외 + 로깅- 뮤터블 기본 인자:
def f(x=[]):→ 공유됨.x=None후x or [] - Pandas에서
iterrows: 느림. 벡터 연산 또는 Polars - Sync 라이브러리를 async 함수에서: 이벤트 루프 블록
os.path대신pathlib를 안 씀: pathlib 훨씬 깔끔- 도커 이미지에
pip install -r requirements.txt: layer caching 망침.uv sync --frozen
마치며 — Python은 AI 시대에 다시 태어났다
Python의 원래 강점은 "생산성과 학습 곡선"이었다. 단점은 "속도와 동시성"이었다.
2024~2025년:
- 속도: uv·Pydantic·Polars·Ruff가 Rust 엔진으로 해결
- 동시성: Free-threading 실험, AsyncIO 성숙
- 타입: 3.12~3.13의 PEP들로 강력해짐
- 생태계: AI 엔지니어링의 표준 언어
"Python이 느리다"는 2020년까지의 이야기였다. 2025년 Python은 **"AI 시대의 공용어이자, Rust 도구들로 빨라진 언어"**다.
시니어 엔지니어가 Python을 안 다룰 수 없는 이유는 명확하다. AI·데이터·API·스크립팅에서 Python은 여전히, 그리고 앞으로도 지배적이다.
다음 글 예고 — "LLM 완전 가이드: Transformer·Attention·RLHF·RAG·Agent·Evaluation"
Season 2 Ep 6은 2025년 엔지니어의 교양, LLM의 내부 구조. 다음 글은:
- Transformer가 실제로 어떻게 동작하는가
- Attention 메커니즘 수식까지
- Pre-training → SFT → RLHF → DPO 흐름
- RAG의 3세대 진화 (Naive → Advanced → Agentic)
- Agent 설계 (ReAct, Plan-and-Execute, Multi-Agent)
- LLM 평가의 진짜 어려움 (LM-as-a-Judge의 한계)
블랙박스를 뜯어보는 시간, 다음 글에서 이어진다.
Python Complete Guide — FastAPI, AsyncIO, Pydantic, uv, Polars, AI Engineering (Season 2 Ep 5, 2025)
Intro — 2025 Python is not 2015 Python
If you learned Python 10 years ago and came back today, the biggest changes:
- uv (2024): a Rust-written unified tool replacing pip, poetry, pyenv, pip-tools. 10-100x faster
- Pydantic v2 (2023): validation engine rewritten in Rust. 5-50x faster
- FastAPI: the de facto standard for type-hint-based API frameworks
- Polars (2024+): Pandas replacement. Rust-based, 10-100x faster, memory efficient
- Python 3.13 (2024/10): Free-threading (No-GIL) experiment, JIT compiler, improved REPL
- LangChain, LangGraph, Pydantic AI (2024+): AI agent stack
- Ruff + mypy/pyright: Lint, Format, type check — all replaced with fast tooling
Python has firmly settled as "the lingua franca of AI engineering," and the entire toolchain is being rewritten in Rust, closing the speed gap.
Part 1 — Python 3.13: the future of the language
1.1 Free-threading (No-GIL) — PEP 703
An Experimental Build removing the 30-year-old GIL (Global Interpreter Lock), released 2024/10:
python3.13t # free-threaded build
- GIL: only one thread executes Python bytecode at a time
- No-GIL: true multi-core parallelism
Limits: currently experimental, ~10% single-thread regression, C extension compatibility issues. Expected to stabilize 2026-2027.
1.2 JIT Compiler — PEP 744
Copy-and-Patch JIT added (experimental):
python --enable-experimental-jit
Initial benchmarks modest (~5%), but long-term potential for PyPy-level performance.
1.3 Improved REPL
The standard REPL in 3.13 is finally usable:
- Multi-line editing
- Syntax highlighting
- Paste mode
- Block history
The things you used PyPy/ipython for, now built in.
1.4 Type System improvements (3.12-3.13)
# PEP 695: Generic Syntax
def first[T](items: list[T]) -> T:
return items[0]
class Stack[T]:
def __init__(self) -> None:
self.items: list[T] = []
# PEP 696: Type Parameter Defaults
def process[T = str](x: T) -> T: ...
# PEP 742: TypeIs
from typing import TypeIs
def is_str(x: object) -> TypeIs[str]:
return isinstance(x, str)
Part 2 — uv: the package manager revolution
2.1 Why uv is a game changer
Rust-based unified tool by Astral (makers of Ruff). Replaces:
- pip → install
- pip-tools → hash-pinned requirements
- pyenv → Python version management
- virtualenv/venv → virtual environments
- poetry → project management
- pipx → CLI tool installation
Speed: a pip install that takes 10 seconds runs in 0.1 seconds with uv.
2.2 Basic usage
# install
curl -LsSf https://astral.sh/uv/install.sh | sh
# initialize project
uv init my-app
cd my-app
# add dependencies (pyproject.toml auto-updated)
uv add fastapi uvicorn
uv add --dev pytest ruff mypy
# run
uv run python main.py
uv run pytest
# Python version management
uv python install 3.13
uv python pin 3.13
# lock file (uv.lock)
uv lock
uv sync
2.3 uv script (single-file Python)
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx",
# "rich",
# ]
# ///
import httpx
from rich import print
print(httpx.get("https://api.github.com").json())
uv run script.py
# dependencies auto-downloaded, then run
Starting new projects with uv is the 2025 standard.
Part 3 — Pydantic v2: type-based validation
3.1 The v2 revolution
pydantic-corerewritten in Rust- 5-50x faster
- Strong constraints via
Annotated - Separated serialization API
3.2 Basic usage
from pydantic import BaseModel, EmailStr, Field
from typing import Annotated
from datetime import datetime
class User(BaseModel):
id: int
email: EmailStr
name: Annotated[str, Field(min_length=1, max_length=100)]
age: Annotated[int, Field(ge=0, le=150)]
created_at: datetime
# validate + convert
user = User.model_validate({
"id": 1,
"email": "alice@example.com",
"name": "Alice",
"age": 30,
"created_at": "2025-01-01T00:00:00Z"
})
# serialize
json_str = user.model_dump_json()
dict_data = user.model_dump()
3.3 Advanced features
from pydantic import field_validator, model_validator
class Post(BaseModel):
title: str
content: str
tags: list[str] = []
@field_validator('title')
@classmethod
def title_not_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError('title cannot be empty')
return v.strip()
@model_validator(mode='after')
def check_tags_limit(self) -> 'Post':
if len(self.tags) > 10:
raise ValueError('too many tags')
return self
3.4 Pydantic Settings
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
api_key: str
debug: bool = False
class Config:
env_file = ".env"
settings = Settings()
The standard for env vars and config management.
Part 4 — FastAPI: the type-safe API standard
4.1 Basic example
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
app = FastAPI()
class UserCreate(BaseModel):
email: str
password: str
class UserResponse(BaseModel):
id: int
email: str
@app.post("/users", response_model=UserResponse)
async def create_user(user: UserCreate) -> UserResponse:
# user is already validated
# response auto-serialized as UserResponse
return UserResponse(id=1, email=user.email)
- Pydantic models are the request/response schemas
- OpenAPI auto-generated (
/docs) - Dependency Injection built-in
4.2 Dependency Injection
async def get_db() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
yield session
async def get_current_user(token: str = Header(...)) -> User:
# token validation logic
return user
@app.get("/me")
async def me(user: User = Depends(get_current_user)) -> UserResponse:
return user
4.3 2025 FastAPI stack
| Purpose | Library |
|---|---|
| Web | FastAPI |
| ORM | SQLAlchemy 2.0 (async), SQLModel |
| DB driver | asyncpg (Postgres), motor (Mongo) |
| Migration | Alembic |
| Queue | Dramatiq, Celery 5.x, Taskiq |
| Cache | Redis + aioredis |
| Test | pytest + httpx |
| Observability | OpenTelemetry + Prometheus |
Part 5 — AsyncIO in practice
5.1 Basic structure
import asyncio
async def fetch(url: str) -> str:
# simulation
await asyncio.sleep(1)
return f"data from {url}"
async def main() -> None:
# sequential
r1 = await fetch("a")
r2 = await fetch("b")
# concurrent (2x faster)
r1, r2 = await asyncio.gather(fetch("a"), fetch("b"))
asyncio.run(main())
5.2 TaskGroup (Python 3.11+)
async def main():
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(fetch("a"))
t2 = tg.create_task(fetch("b"))
t3 = tg.create_task(fetch("c"))
# on context exit, all tasks complete
# if any raises, others cancelled + ExceptionGroup
print(t1.result(), t2.result(), t3.result())
TaskGroup beats asyncio.gather: structured concurrency, auto-cancel, clear errors.
5.3 asyncio.timeout (Python 3.11+)
async def fetch_with_timeout():
async with asyncio.timeout(5.0):
return await slow_operation()
5.4 Five AsyncIO pitfalls
- Mixing blocking code: use
httpx.AsyncClientinstead ofrequests.get - Nesting
asyncio.run: event loop conflict.nest_asynciois an anti-pattern - Failing to keep task references: store
asyncio.create_taskresults somewhere or GC eats them - CPU-bound work: offload via
run_in_executorto thread/process time.sleepinsideasync def: useasyncio.sleep
Part 6 — Polars: 2025 Pandas replacement
6.1 Why Polars
| Item | Pandas | Polars |
|---|---|---|
| Engine | NumPy (C) | Rust + Arrow |
| Speed | baseline | 5-100x faster |
| Memory | heavy | ~50% of Pandas |
| Parallelism | almost none | auto multi-thread |
| Lazy execution | no | yes (optimizer) |
| Large data | hard | streaming supported |
6.2 Basic usage
import polars as pl
df = pl.read_csv("data.csv")
result = (
df
.filter(pl.col("age") > 30)
.group_by("country")
.agg([
pl.col("salary").mean().alias("avg_salary"),
pl.col("id").count().alias("count"),
])
.sort("avg_salary", descending=True)
)
6.3 Lazy Evaluation
lazy_df = pl.scan_csv("huge.csv") # not actually read
result = (
lazy_df
.filter(pl.col("year") == 2024)
.group_by("category")
.agg(pl.col("amount").sum())
.collect() # now executes (optimizer plans)
)
The query optimizer plans execution like a SQL DBMS.
6.4 2025 migration guide
- New project: Polars by default
- Existing Pandas project: incremental migration (
df.to_pandas()bridge) - Visualization may still be easier with Pandas (matplotlib/seaborn)
- ML library compatibility still favors Pandas (improving)
Part 7 — 2025 AI engineering stack
7.1 Current state of LangChain
LangChain exploded in 2023, had growing pains in 2024, and 2025 is reorganized around LangGraph.
- LangChain Core: base abstractions (Runnable)
- LangGraph: state-based agent graph (2025 standard)
- LangSmith: observability and evaluation
7.2 LangGraph example
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
class State(TypedDict):
messages: Annotated[list, operator.add]
def agent(state: State):
response = llm.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state: State) -> str:
last = state["messages"][-1]
if last.tool_calls:
return "tools"
return END
workflow = StateGraph(State)
workflow.add_node("agent", agent)
workflow.add_node("tools", tool_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("tools", "agent")
app = workflow.compile()
Directed Graph + state machine model. Ideal for complex agent flows.
7.3 Pydantic AI (2024+)
A type-safe agent framework by the Pydantic team. Lightweight, intuitive.
from pydantic_ai import Agent
from pydantic import BaseModel
class WeatherResponse(BaseModel):
city: str
temp_c: float
condition: str
agent = Agent(
'openai:gpt-4o',
result_type=WeatherResponse,
system_prompt='You are a weather assistant.',
)
result = await agent.run('Weather in Seoul?')
print(result.data) # typed as WeatherResponse
7.4 2025 AI stack recommendations
| Situation | Recommendation |
|---|---|
| Complex multi-step agent | LangGraph |
| Simple type-safe agent | Pydantic AI |
| RAG-focused | LlamaIndex |
| Minimal production deps | OpenAI SDK directly |
| Matching a TS team | Vercel AI SDK (TS) |
Part 8 — Modern Python dev environment
8.1 2025 standard toolchain
# pyproject.toml
[project]
name = "my-app"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.115",
"pydantic>=2.9",
"uvicorn>=0.30",
]
[dependency-groups]
dev = [
"pytest>=8",
"ruff>=0.7",
"mypy>=1.13",
]
[tool.ruff]
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B", "S", "C4", "PL"]
[tool.mypy]
strict = true
8.2 Ruff — 10-100x faster linter/formatter
- Replaces Flake8, Black, isort, pyupgrade, pylint
- Written in Rust
- 800+ rules built-in
8.3 Type checker choice
| Tool | Traits |
|---|---|
| mypy | oldest, slow, conservative |
| pyright | MS-made, fast, strict |
| pyre | Meta-made, server mode |
| ty | Astral-made (2025), Rust-based (in dev) |
2025 pick: pyright for new, mypy for existing. Waiting on ty.
8.4 Testing
# pytest + httpx for FastAPI
import pytest
from httpx import AsyncClient, ASGITransport
from myapp.main import app
@pytest.mark.asyncio
async def test_create_user():
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
r = await ac.post("/users", json={"email": "a@b.com", "password": "pw"})
assert r.status_code == 200
Part 9 — Where Python shines vs not
9.1 Shines
- AI/ML: PyTorch, JAX, scikit-learn, Hugging Face
- Data engineering: Airflow, dbt, Dagster
- API servers: FastAPI productivity
- Scientific computing: NumPy, SciPy
- Scripting/automation: glue language
- Education: gentle curve
9.2 Does not shine
- CPU-bound high performance: Rust/Go/C++
- Mobile apps: Swift/Kotlin
- Embedded: C/Rust
- Game engines: C++
- Browser: JS/TS (Pyodide is niche)
2025 reality: Python is the standard for "AI, data, API" but not "for everything."
Part 10 — 6-month Python mastery roadmap
Month 1: 3.13 fundamentals
- Full type hints (PEP 695, Union, Literal, Protocol)
- Project management with uv
- Ruff + mypy/pyright setup
Month 2: Pydantic + FastAPI
- Deep Pydantic v2 (Annotated, Validators)
- Building a real FastAPI
- Dependency Injection patterns
Month 3: AsyncIO
- TaskGroup, asyncio.timeout
- Async DB with httpx, asyncpg
- Concurrency limits (Semaphore)
Month 4: Data processing
- Polars mastery
- SQL + SQLAlchemy 2.0 async
- DuckDB + Parquet
Month 5: AI engineering
- OpenAI/Anthropic SDK
- LangGraph or Pydantic AI
- Vector DB (Qdrant, Pinecone, pgvector)
- RAG pipeline construction
Month 6: Operations
- Docker + Kubernetes deployment
- Observability (OpenTelemetry, Prometheus)
- CI/CD (GitHub Actions)
- Profiling (py-spy, scalene)
Part 11 — 12-item Python checklist
- Know the 4 tools uv replaces (pip/poetry/pyenv/pip-tools)
- Know Pydantic v1 vs v2 major changes
- Know FastAPI Dependency Injection usage
- Know TaskGroup vs asyncio.gather differences
- Know the benefits of Polars Lazy Evaluation
- Know how to use Protocol and TypeIs type hints
- Know the SQLAlchemy 2.0 async style
- Know the meaning of GIL and Free-threading
- Know the relationship between LangGraph and LangChain
- Know the tools Ruff replaces
- Can manage config via Pydantic Settings
- Can test async APIs via pytest + httpx
Part 12 — 10 Python anti-patterns
pip install+requirements.txtonly: weak reproducibility. Useuv+uv.lock- Writing Python without type hints: leaving bugs mypy/pyright would catch
from X import *: namespace pollution- Debugging with
print(): useloggingorrich except Exception: pass: silent failure. Use specific exceptions + logging- Mutable default args:
def f(x=[]):shares state. Usex=Nonethenx or [] iterrowsin Pandas: slow. Use vectorized ops or Polars- Sync libraries in async functions: blocks the event loop
- Not using
pathlibinstead ofos.path: pathlib is much cleaner pip install -r requirements.txtin Docker images: breaks layer caching. Useuv sync --frozen
Closing — Python is reborn in the AI era
Python's original strengths were "productivity and learning curve." Its weaknesses were "speed and concurrency."
2024-2025:
- Speed: solved by uv, Pydantic, Polars, Ruff with Rust engines
- Concurrency: free-threading experiment, AsyncIO matured
- Types: strengthened by 3.12-3.13 PEPs
- Ecosystem: the standard language for AI engineering
"Python is slow" was a pre-2020 story. In 2025 Python is "the lingua franca of the AI era, sped up by Rust tools."
Senior engineers cannot avoid Python for a reason. In AI, data, API, and scripting, Python remains — and will remain — dominant.
Next — "LLM Complete Guide: Transformer, Attention, RLHF, RAG, Agent, Evaluation"
Season 2 Ep 6 is the 2025 engineer's core literacy: the internals of LLMs. Next:
- How Transformer actually works
- Attention mechanism down to the equations
- Pre-training to SFT to RLHF to DPO flow
- Three generations of RAG (Naive to Advanced to Agentic)
- Agent design (ReAct, Plan-and-Execute, Multi-Agent)
- The real difficulty of LLM evaluation (limits of LM-as-a-Judge)
Time to pry open the black box. See you next.