4 분 소요

2025 10월 7일에 정식으로 릴리즈된 Python 3.14에서 눈여겨볼 주요 업데이트 사항을 정리합니다.

1. t-string (Template String)

python 3.14에서는 새로운 문자열 포매팅 문법인 t-string이 도입되었습니다. f-string은 코드 실행 시점에 바로 문자열로 완성됩니다. 따라서 변수에 할당된 값이 삽입되어 최종적인 str 객체를 만듭니다.

반면, t-string은 string.templatelib.Template 객체를 반환합니다.

from string.templatelib import Interpolation
import html

user_input = '<script>alert("악성코드")</script>'
template = t"<p>Hello {user_input}</p>"

def html_render(template):
    result = ""
    for part in template:
        if isinstance(part, Interpolation):
            result += html.escape(str(part.value))
        else:
            result += str(part)
    return result

print(html_render(template)) 
# 결과 : <p>Hello &lt;script&gt;alert(&quot;Hacked!&quot;)&lt;/script&gt;</p>

t-string을 사용하면 라이브러리들은 위와 같이 삽입된 값을 자동으로 인코딩하거나 escaping 하거나 검증하는 로직을 삽입할 수 있고, SQL 쿼리 생성, HTML 마크업 생성, 로깅 메시지 생성 등 다양한 도메인 특화 환경에서 보안성과 유연성을 높일 수 있습니다.

2. Deferred Annotations

Python 3.14부터 애너테이션 처리 방식이 크게 개선되었습니다. 가장 핵심적인 변화는 전방 참조(forward reference)지연 평가(deferred annotations)의 도입입니다.

def some_function(x: Node) -> Node:
    return x.next_node


class Node:
    def __init__(self,):
        self.next_node = None

if __name__ == "__main__":
    print('성공!')

"""
python 3.13:
Traceback (most recent call last):
  File "/Users/euntaeklee/python/Workspace/concurrency/annotations.py", line 1, in <module>
    def some_function(x: Node) -> Node:
                         ^^^^
NameError: name 'Node' is not defined. Did you mean: 'None'?
-------------------------------------------------------------------------
python 3.14:
성공!
"""
  • Python 3.14부터 애너테이션은 지연 평가되며, 전방 참조를 안전하게 사용할 수 있음
  • 덕분에 모듈 의존성 문제 등을 자연스럽게 해결 가능
  • 기존 Python 3.13까지는 이런 코드가 바로 NameError를 발생시켰다는 점에서 큰 차이가 있음

3. Multiple Interpreters & InterpreterPoolExecutor

Python 3.14에서는 멀티 인터프리터(Multiple Interpreters)와 이를 쉽게 활용할 수 있는 InterpreterPoolExecutor가 정식으로 도입되었습니다. 이 기능은 GIL(Global Interpreter Lock) 한계를 우회하고, CPU 집약적인 작업을 병렬로 실행할 수 있는 새로운 방식입니다.

Multiple Interpreters

Python은 전통적으로 하나의 GIL(Global Interpreter Lock) 때문에, 멀티스레드 CPU 연산 성능이 제한적입니다. Python 3.14부터는 서브 인터프리터(Sub-Interpreter) 를 만들어, 각각 독립된 GIL을 가진 인터프리터에서 코드 실행이 가능해졌습니다.

from concurrent import interpreters

interpreter = interpreters.create()
interpreter.exec('print("hello from sub-interpreter")')

def square(n):
    return n * n

print(interpreter.call(square, 12))
print(square(12))
  • interpreters.create()로 새로운 인터프리터 생성
  • 각 인터프리터는 독립된 GIL과 메모리 공간을 가지므로, CPU 바운드 작업을 병렬로 수행 가능
  • interpreter.exec()와 interpreter.call()로 인터프리터 내 코드 실행 및 함수 호출 가능

InterpreterPoolExecutor

InterpreterPoolExecutor는 멀티 인터프리터를 활용한 병렬 실행을 쉽게 구현할 수 있는 고수준 API입니다. ThreadPoolExecutor와 사용법이 유사하며, 내부적으로 각 작업을 별도의 서브 인터프리터에서 실행합니다.

import math, time
from concurrent.futures import InterpreterPoolExecutor, as_completed, ThreadPoolExecutor

results = []

start = time.perf_counter()

with InterpreterPoolExecutor(max_workers=4) as excutor:
    futures = [excutor.submit(math.factorial, i) for i in range(20000, 22000)]

    for future in as_completed(futures):
        results.append(future.result())
    
end = time.perf_counter()

print(f"Multi Interpreter : {end-start}")
  • CPU 바운드 작업인 math.factorial을 4개의 인터프리터에서 병렬 처리
  • GIL이 각 인터프리터마다 독립적이므로, 멀티스레드보다 높은 CPU 활용 가능

성능 비교

같은 작업을 멀티 인터프리터, 멀티 스레드, 싱글 스레드에서 비교:

from concurrent.futures import ThreadPoolExecutor
import math, time

results = []

start = time.perf_counter()

# Multi Thread
with ThreadPoolExecutor(max_workers=4) as executor:
    futures = [executor.submit(math.factorial, i) for i in range(20000, 22000)]

    for future in as_completed(futures):
        results.append(future.result())

end = time.perf_counter()

print(f"Multi Thread : {end-start}")

results = []

start = time.perf_counter()

# Single Thread
for i in range(20000, 22000):
    results.append(math.factorial(i))

end = time.perf_counter()

print(f"Single Thread : {end-start}")
방식 소요시간 한계
Multi Interpreter 6.958329709013924 인터프리터 생성 비용 존재, 메모리 사용 증가
Multi Thread 27.528747166972607 CPU 바운드 작업에서 GIL 때문에 성능 제한
Single Thread 27.57662904204335 병렬 처리 불가, 느림

Python 3.14의 멀티 인터프리터 기능은 GIL 한계를 극복하고 CPU 바운드 작업을 병렬로 안전하게 수행할 수 있는 혁신적인 방법입니다.

4. Free-Threaded Python

Python 3.14t는 GIL(Global Interpreter Lock) 제약을 제거한 Free-thread 모드를 지원하는 버전입니다. 이는 Python이 전통적으로 가지고 있던 멀티스레드 CPU 바운드 성능 한계를 완전히 혁신하는 기능입니다.

Python 3.14t(Free-thread) 특징

GIL 제거

  • 각 스레드가 독립적으로 Python 바이트코드를 실행 가능
  • 스레드 간 락 없이 CPU 바운드 병렬 처리 가능

완전한 멀티스레드 병렬

  • ThreadPoolExecutor, concurrent.futures, asyncio 등 기존 스레드 API와 호환
  • CPU 코어 수만큼 실제 병렬 처리 가능

기존 코드 호환

  • 대부분의 기존 Python 코드는 변경 없이 사용 가능
  • 단, C 확장 모듈에서 GIL 의존 코드는 주의 필요

성능 비교

기존 코드에서 python version을 3.14 -> 3.14t로 변경

방식 소요시간
Multi Interpreter 7.407573916018009
Multi Thread 6.850294167059474
Single Thread 27.134682000032626

Free-thread Python(3.14t)에서는 GIL이 제거되어, 멀티스레드 CPU-bound 작업에서 기존보다 훨씬 빠른 성능을 기대할 수 있습니다.

주의사항

  • C 확장 모듈에서 GIL 의존 코드는 Free-thread에서 예상치 못한 동작 가능
  • 스레드 안전성을 보장하지 않는 전역 상태나 라이브러리는 주의 필요
  • 디버깅이나 프로파일링 시 기존 Python과 동작 차이 발생 가능

AsyncIO CLI Tool

Python 3.14에서는 asyncio 모듈과 함께 CLI(Command Line Interface) 도구를 제공하여, 실행 중인 비동기 프로그램의 상태를 쉽게 확인할 수 있습니다.

AsyncIO 비동기 코드 예제 :

import os, asyncio

async def function1():
    await asyncio.sleep(15)
    print('Hello Form 1')

async def function2():
    await asyncio.sleep(20)
    print('Hello Form 2')

async def main():
    await asyncio.gather(
        function1(),
        function2()
    )

if __name__ == '__main__':
    print(os.getpid())
    asyncio.run(main())

AsyncIO CLI 사용법

Python 3.14에서는 asyncio CLI를 통해 실행 중인 비동기 프로그램의 상태를 실시간으로 확인할 수 있습니다.

2-1. 프로세스 상태 확인

uv run asynccli.py  # 코드 실행 후 PID 확인
  • 위 명령으로 AsyncIO 프로그램 실행
  • 프로그램 실행 시 PID를 출력 → CLI에서 해당 PID를 이용
sudo .venv/bin/python -m asyncio ps "PID"

tid        task id              task name            coroutine stack                                    awaiter chain                                      awaiter name    awaiter id
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
7242000    0x451ad180210        Task-1               main                                                                                                                  0x0
7242000    0x451ad180410        Task-2               sleep -> function1                                 main                                               Task-1          0x451ad180210
7242000    0x451ad180610        Task-3               sleep -> function2                                 main                                               Task-1          0x451ad180210
  • ps 명령으로 해당 PID의 비동기 루프 상태 확인
  • 현재 실행 중인 태스크, 스케줄된 코루틴 등 상세 정보 출력

2-2. 프로세스 트리 확인

sudo .venv/bin/python -m asyncio pstree "PID"

└── (T) Task-1
    └──  main /Users/username/Workspace/asynccli.py:12
        ├── (T) Task-2
        │   └──  function1 /Users/username/python/Workspace/asynccli.py:4
        │       └──  sleep /Users/username/.local/share/uv/python/cpython-3.14.0+freethreaded-macos-aarch64-none/lib/python3.14t/asyncio/tasks.py:702
        └── (T) Task-3
            └──  function2 /Users/username/python/Workspace/asynccli.py:8
                └──  sleep /Users/username/.local/share/uv/python/cpython-3.14.0+freethreaded-macos-aarch64-none/lib/python3.14t/asyncio/tasks.py:702
  • pstree 명령으로 해당 PID의 코루틴/태스크 구조 트리 시각화
  • 어떤 코루틴이 실행 중인지, 어떤 순서로 스케줄되어 있는지 확인 가능

Python 3.14 AsyncIO CLI는 비동기 프로그램을 실행 중에도 실시간 모니터링하고, 코루틴 구조를 트리 형태로 시각화할 수 있는 강력한 도구입니다.

느낀점

Python 3.14는 GIL-Free, 서브 인터프리터, InterpreterPoolExecutor, Async CLI 등 병렬·동시성 측면에서 Python 역사상 가장 큰 구조적 진보를 보여주었습니다. 이제 Python도 멀티코어 성능을 제대로 활용할 수 있는 시대가 열렸지만, 아직 일부 프레임워크와 라이브러리와의 호환성 문제는 남아 있습니다. 그러나 지속적인 업데이트를 통해 점진적으로 개선될 것이 기대됩니다.