Backend / Python core / 35_python_314_features.md

What's new in Python 3.12-3.14

Updated 5 interview angles 4 min read source
On this page7
  1. Python 3.14 — the headline changes
  2. Template strings (PEP 750)
  3. Deferred annotations (PEP 649)
  4. Python 3.13 recap
  5. Python 3.12 recap
  6. The support window
  7. Interview angle

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 The GIL, and free-threaded Python. 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.

python
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.

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

# 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.

python
class Node:
    # works without quotes or __future__ import
    parent: Node | None
    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:

python
# 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 TypeVar and generics.

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 5

  • “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.