After an eight-day delay, Python 3.15 finally shipped on October 9, and Python 3.16’s first alpha followed only four days later. The alpha arrived alongside two new PEPs that propose bringing ?. and ?? to Python, more than ten years after the last attempt stalled.

You’ll also read about eight intermediate certificate authorities whose private keys turned out to be recoverable by anyone, which sent pip, certifi, uv, and CPython itself scrambling to ship fixes. Meanwhile, pandas 3.1 and Polars 2.0 both landed, Ruff stopped nagging you about most naive datetimes, and Anthropic’s new API version dropped temperature.

That’s plenty to cover, so dive into this month’s Python news!

Python 3.15 Is Finally Here

After last month’s surprise third release candidate, Python 3.15.0 landed on October 9, eight days late and without further drama. Release manager Hugo van Kemenade’s announcement counts 5,643 commits from 1,012 contributors since Python 3.14.

You already know the headliners if you’ve followed Real Python’s coverage this year: lazy imports, frozendict, the new sentinel built-in, and UTF-8 as the default encoding. For a guided tour, Python 3.15: Cool New Features for You to Try and the What’s New in Python 3.15 video course walk through them with examples.

The Small Features You’ll Use Every Day

However, two of the smaller changes may well be the ones you end up using most often. The first is PEP 798, which lets you unpack inside a comprehension. Flattening a list of lists no longer needs a nested loop or itertools.chain():

Language: Python
>>> lists = [[1, 2], [3], [4, 5]] >>> [*x for x in lists] [1, 2, 3, 4, 5] >>> {*x for x in lists} {1, 2, 3, 4, 5} >>> profiles = [{"a": 1}, {"b": 2}, {"a": 3}] >>> {**p for p in profiles} {'a': 3, 'b': 2} 

The star unpacks one level per item. Dictionary unpacking follows the same rules as {**a, **b}, so later keys win, which is why "a" ends up with the value 3.

The second is Tachyon, the sampling profiler that ships in the new profiling package from PEP 799. It samples your program’s stack from the outside, so it adds very little overhead and can even attach to a process that’s already running. You don’t need to change any code to point it at a script. Take slow.py, which computes a Fibonacci number recursively:

Language: Python
def fib(n): return n if n < 2 else fib(n - 1) + fib(n - 2) print(fib(32)) 

Running it under Tachyon prints the script’s output, followed by the profile. Your timings will differ:

Language: Shell
$ python -m profiling.sampling run slow.py 2178309 Captured 298 samples in 0.30 seconds Sample rate: 1000.00 samples/sec Error rate: 36.58 Profile Stats:  nsamples sample% tottime (ms) cumul% cumtime (ms) filename:lineno(function)  187/189 98.9 187.000 100.0 189.000 slow.py:2(fib) 

Tachyon correctly blames nearly every sample on fib(), whose return statement is on line 2 of the script. The percentages only count the samples Tachyon managed to read, and the error rate shows how many it missed, which is why the table adds up to 189 samples rather than 298.

Add --flamegraph -o profile.html and you get an interactive flame graph instead. Real Python’s guide to profiling.sampling covers attaching to live processes and the different sampling modes.

If you install Python from python.org, then you’ll notice some changes there, too. The Windows 64-bit binaries now use the tail-calling interpreter, which the What’s New document credits with a speedup of fifteen to twenty percent on pyperformance over the older switch-case interpreter on its test machine. The macOS installer now installs free-threading support by default, so the GIL-free build is already there when you want to try it.

How Fast Is Python 3.15?

Miguel Grinberg ran his yearly benchmark, How Fast Is Python 3.15?, against the third release candidate, a few days before the final release. He concludes:

Python 3.15 is at best a fairly minor improvement over 3.14 in terms of performance. (Source)

The runtime of his recursive fib(40) benchmark only dropped from 7.17 seconds on 3.14 to 6.94 seconds on 3.15, about a three-percent gain. Turning on the experimental JIT shaved that down to 5.76 seconds, but Grinberg still advises against using it in production.

Free threading is where the numbers look most impressive. Grinberg also ran fib(40) in four threads at once, so the test does four times the work of the single-threaded run.

With the GIL, the threads took turns, and the standard build needed 32.5 seconds. The free-threaded build ran them in parallel and finished in 7.2 seconds, about four and a half times faster and roughly the time that one fib(40) takes on its own.

Read the full article at https://realpython.com/python-news-november-2026/ »


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]