backend / python core / 35_python_314_features.md

What's new in Python 3.12-3.14

5 interview angles 4 min read source

What’s new in Python 3.12-3.14

The version-currency file. Python 3.14 shipped October 2025; 3.14.x is current as of 2026-08. Knowing what landed recently is a cheap way to sound current, and misdescribing it is a cheap way not to.

Python 3.14 — the headline changes

Feature PEP What it means
Free-threaded build officially supported 779 no longer experimental; still an optional build
Multiple interpreters in the stdlib 734 concurrent.interpreters; a fourth concurrency model
Template strings (t-strings) 750 safe interpolation primitives
Deferred annotation evaluation 649 annotations are lazy by default
compression.zstd 784 Zstandard in the stdlib
Zero-overhead external debugger attach to a running process safely

The first two are covered in ../04_async_concurrency/01_gil.md. The next two matter for everyday code.

Template strings (PEP 750)

An f-string evaluates to a str immediately. A t-string evaluates to a Template object holding the static parts and the interpolated values separately — so a library can process them before they’re combined.

from string.templatelib import Template

name = "Robert'); DROP TABLE students;--"
t = t"SELECT * FROM users WHERE name = {name}"

type(t)          # string.templatelib.Template, NOT str

The point: the boundary between literal text and interpolated data survives, so a library can escape, parameterise or validate correctly. That’s exactly the boundary f-strings destroy, which is why f-strings in SQL and HTML are injection bugs.

# f-string: the boundary is gone before your library sees it
query = f"SELECT * FROM users WHERE name = '{name}'"   # SQL injection

# t-string: the library receives the parts and can parameterise
query = sql(t"SELECT * FROM users WHERE name = {name}")

t-strings are a primitive for library authors, not a drop-in fix. The value arrives as libraries (database drivers, HTML templating, logging) add Template support. The interview-relevant point is understanding why the separation matters.

Deferred annotations (PEP 649)

Annotations are now evaluated lazily, on demand, rather than at definition time.

class Node:
    parent: Node | None      # works without quotes or __future__ import
    children: list[Node]

What changes:

  • Forward references just work. No more string quoting or from __future__ import annotations.
  • Import cost drops — annotations aren’t evaluated unless something asks for them.
  • typing.get_type_hints() still resolves them to real objects, unlike the __future__ behaviour which left them as strings.

The __future__ import annotations approach is now largely unnecessary. If you see it in new code, it’s a habit rather than a requirement.

Python 3.13 recap

Feature Note
Experimental free-threaded build promoted to official in 3.14
Experimental JIT (copy-and-patch) still experimental
Improved REPL multiline editing, colour
Better error messages continues a long-running trend
dbm.sqlite3 new default dbm backend

Python 3.12 recap

Still widely deployed, so worth knowing:

Feature PEP Note
Type parameter syntax 695 def f[T](x: T) -> T: — no explicit TypeVar
f-string grammar formalised 701 nested quotes, multiline, comments allowed
type statement 695 type Alias = int | str
Per-interpreter GIL 684 the groundwork for PEP 734
itertools.batched chunking without a recipe
@override decorator 698 catches renamed base methods

PEP 695 generics are the one to use in new code:

# 3.12+
def first[T](items: list[T]) -> T | None:
    return items[0] if items else None

type Json = dict[str, "Json"] | list["Json"] | str | int | float | bool | None

See typing/02_typevar_generics.md.

The support window

Version Status (2026-08)
3.9 and earlier EOL
3.10, 3.11 security fixes only
3.12, 3.13 supported
3.14 current

Anything describing 3.7-3.9 idioms as “modern” is several years stale. New projects should target 3.13 or 3.14.

Interview angle

  • “What’s new in recent Python?” — 3.14: free-threading officially supported (PEP 779), stdlib subinterpreters (PEP 734), t-strings (PEP 750), deferred annotations (PEP 649). 3.12: PEP 695 type parameter syntax. Naming the PEP numbers is optional; knowing what each does is not.
  • “What problem do t-strings solve?” — they preserve the boundary between literal text and interpolated values, so a library can escape or parameterise correctly. F-strings destroy that boundary before the library sees it, which is why f-string SQL is an injection bug. t-strings are a primitive for library authors, not an automatic fix.
  • “What changed with annotations in 3.14?” — PEP 649 made evaluation lazy, so forward references work without quotes or the __future__ import, imports get cheaper, and get_type_hints() still resolves to real objects rather than strings.
  • “How do you write a generic function in modern Python?” — PEP 695 syntax: def first[T](items: list[T]) -> T | None. No explicit TypeVar declaration, and type X = ... for aliases.
  • “Which Python version would you target for a new service?” — 3.13 or 3.14. 3.9 and earlier are EOL, 3.10 and 3.11 are security-only. If you want free-threading, 3.14 with the caveat that it’s an optional build and your C extensions need to support it.