---
title: "Python 3.15에서 헤드라인은 못 탔지만 꽤 쓸만한 기능들"
published: 2026-05-21T11:10:11.000Z
canonical: https://jeff.news/article/3127
---
# Python 3.15에서 헤드라인은 못 탔지만 꽤 쓸만한 기능들

Python 3.15에는 lazy imports나 Tachyon profiler 같은 큰 기능 말고도 실무에서 바로 체감될 만한 작은 개선들이 들어가. TaskGroup 취소, 컨텍스트 매니저 데코레이터 개선, 스레드 안전 이터레이터처럼 평소 애매하게 불편했던 지점들이 꽤 깔끔해졌어.

- Python 3.15는 큰 기능만 있는 릴리스가 아님.
  - lazy imports나 Tachyon profiler처럼 눈에 띄는 기능도 있지만, 이번 글은 헤드라인을 못 탄 작은 변화들을 훑음.
  - 작은 기능이라도 실무에서 반복적으로 밟던 발목 지점을 없애는 경우가 있어서, 이런 게 은근 오래 감.

## asyncio 쪽은 TaskGroup 취소가 깔끔해짐

- Python 3.15에서는 asyncio TaskGroup을 더 우아하게 취소할 수 있음.
  - TaskGroup은 여러 비동기 작업을 구조적으로 묶어 실행하는 기능.
  - 기존에는 그룹 실행 중 어떤 신호를 받아 전체를 멈추려면, 커스텀 예외를 던지고 ExceptionGroup에서 걸러내는 식의 우회가 필요했음.

- 새 TaskGroup.cancel은 그 우회를 없애줌.
  - 그룹 안의 작업을 취소하되, 일부러 예외를 만들어 흐름 제어에 쓰지 않아도 됨.
  - “설명할 게 거의 없을 만큼 단순하다”는 원문 표현이 딱 맞는 변화.

> [!TIP]
> asyncio 코드에서 TaskGroup 종료를 위해 커스텀 예외를 던지고 suppress로 걸러내던 패턴이 있다면, Python 3.15부터는 TaskGroup.cancel로 단순화할 여지가 있음.

## 컨텍스트 매니저 데코레이터가 제너레이터 수명주기까지 감쌈

- Python에서는 컨텍스트 매니저를 데코레이터처럼 쓸 수 있음.
  - 예를 들어 특정 블록의 실행 시간을 재는 컨텍스트 매니저를 함수 데코레이터로 붙이는 식.
  - 이 기능 자체는 Python 3.3부터 가능했음.

- 문제는 이 방식이 이터레이터, 비동기 함수, 비동기 이터레이터에서는 잘 안 맞았다는 점.
  - 일반 함수는 호출 시 실제 실행이 바로 일어나지만, 제너레이터 함수는 호출하면 제너레이터 객체만 반환하고 본문 실행은 나중에 시작됨.
  - 코루틴 함수와 비동기 제너레이터도 비슷하게 “호출 시점”과 “실제 실행 수명주기”가 분리됨.
  - 그래서 데코레이터가 감싸야 할 전체 작업이 아니라 객체 생성 순간만 감싸고 끝나는 함정이 생겼음.

- Python 3.15의 ContextDecorator는 감싸는 함수의 타입을 확인해 전체 수명주기를 덮도록 바뀜.
  - 이터레이터면 반복이 끝날 때까지, 코루틴이면 await가 끝날 때까지, 비동기 제너레이터면 비동기 반복 수명까지 커버하는 식.
  - 글쓴이는 이 변화 덕분에 컨텍스트 매니저가 데코레이터를 만드는 가장 좋은 방법이 됐다고 평가함. 살짝 과감한 주장인데, 발목 포인트가 줄어든 건 맞음.

## 스레드 안전 이터레이터가 표준 라이브러리에 들어옴

- Python 이터레이터는 기본적으로 스레드 안전하지 않음.
  - 여러 스레드가 같은 이터레이터를 동시에 소비하면 값이 건너뛰어지거나 내부 상태가 깨질 수 있음.
  - free-threading 쪽 흐름까지 생각하면 이 문제가 더 중요해짐.

- Python 3.15에는 threading.serialize_iterator가 추가됨.
  - 기존 이터레이터를 감싸서 여러 스레드가 안전하게 값을 하나씩 가져가도록 만듦.
  - generator 함수 결과에 이 처리를 적용하는 threading.synchronized_iterator 데코레이터도 있음.

- threading.concurrent_tee도 추가됨.
  - 값을 나눠 갖는 게 아니라, 여러 이터레이터에 같은 값을 복제해 전달하는 용도.
  - 예전에는 이런 동기화를 하려면 Queue 중심으로 구조를 바꾸는 일이 많았는데, 이제 이터레이터 추상화를 유지한 채 해결할 수 있는 선택지가 생김.

## 보너스 기능도 소소하게 흥미로움

- collections.Counter에 xor 연산이 들어감.
  - Counter는 단순 빈도 카운터처럼 보이지만, 집합처럼 교집합·합집합류 연산도 제공해 왔음.
  - Python 3.15에서는 대칭 차집합에 해당하는 xor까지 추가됨.
  - 글쓴이는 실제 사용처가 바로 떠오르진 않지만, 완성도 측면에서는 납득된다는 반응.

- frozendict 추가로 JSON 타입 전체를 불변·해시 가능 형태로 표현할 수 있게 됨.
  - JSON의 배열, 불리언, 숫자, null, 문자열은 이미 불변 표현이 가능했지만 객체는 일반 dict라 해시 불가능했음.
  - frozendict가 들어오면서 JSON object까지 불변 구조로 다룰 수 있게 됨.

---

## 기술 맥락

- 이번 변화들은 대부분 “새로운 패러다임”보다는 기존 추상화의 빈틈을 메우는 쪽이에요. TaskGroup, ContextDecorator, iterator는 이미 많이 쓰이는데, 특정 상황에서만 갑자기 복잡해지는 부분이 있었거든요.

- TaskGroup.cancel이 중요한 이유는 예외를 흐름 제어용으로 덜 쓰게 해준다는 점이에요. 비동기 코드에서 ExceptionGroup까지 얽히면 디버깅이 금방 피곤해지는데, 취소 의도를 API로 직접 표현하면 코드 읽는 사람이 훨씬 덜 헷갈려요.

- ContextDecorator 개선은 라이브러리 작성자에게 특히 의미가 있어요. 데코레이터 하나가 일반 함수와 제너레이터, 비동기 함수에서 다르게 동작하면 사용자는 버그를 자기 코드 문제로 오해하기 쉽거든요.

- 스레드 안전 이터레이터는 free-threading 시대를 준비하는 성격도 있어요. 이터레이터라는 깔끔한 추상화를 유지하면서 동시 소비를 안전하게 만들 수 있으니, 기존 코드를 Queue 중심으로 크게 바꾸지 않아도 되는 장점이 있어요.

## 핵심 포인트

- asyncio TaskGroup에 예외 없이 그룹을 취소하는 cancel 기능이 추가됨
- ContextDecorator가 제너레이터·코루틴·비동기 제너레이터의 전체 수명주기를 감싸도록 개선됨
- threading.serialize_iterator로 이터레이터를 스레드 안전하게 감쌀 수 있음
- collections.Counter에 xor 연산이 추가됨
- frozendict 추가로 JSON 타입 전체를 불변·해시 가능 형태로 표현할 수 있게 됨

## 인사이트

Python 3.15의 작은 기능들은 화려하진 않지만, 라이브러리 작성자나 동시성 코드를 만지는 사람에겐 꽤 반가운 변화야. 특히 컨텍스트 매니저를 데코레이터로 쓸 때 생기던 수명주기 함정이 줄어든 건 실무 버그를 조용히 줄여줄 가능성이 큼.
