Python 3.15, explained with short examples
Python 3.15 is out. The standard interpreter is about as fast as Python 3.14, so this release is more about useful new tools than a huge speed jump.
Here are the changes I care about, with the reason each one helps.
Lazy imports
Large Python programs often load modules they never use. That makes command-line tools feel slow before they have even started doing work.
Python 3.15 lets an import wait until the code uses it:
lazy import json
print("Ready") # json has not loaded yetpayload = json.dumps({"ok": True}) # json loads hereThis is useful for CLIs with optional plugins or heavy dependencies. A small command no longer needs to load every library the full application might use. There is one catch: an import error or import-time setup now happens when the name is first used, not at startup. PEP 810 explains the details.
Easier ways to join data
Flattening nested lists used to need a double loop. The new unpacking syntax makes the intent more obvious:
groups = [[1, 2], [3, 4]]flat = [*group for group in groups]
# [1, 2, 3, 4]It also works for sets and dictionaries. This saves a little code, but the real benefit is that you can read “join these groups” right where it happens. PEP 798 has more examples.
A profiler in the standard library
When a service or script is slow, guessing is expensive. Python 3.15 includes a sampling profiler called Tachyon, which can attach to a process that is already running:
# Replace 1234 with the process IDuv run --python 3.15 python -m profiling.sampling attach 1234It samples the call stack while the program runs, then reports where it spent its time. That makes it easier to find the code worth fixing before you start optimizing the wrong thing.
The experimental JIT
The normal Python 3.15 interpreter is only a small step ahead of 3.14. The experimental JIT is more interesting. In Miguel Grinberg’s benchmark, it made one Fibonacci test about 20% faster and one bubble-sort test about 28% faster.
Those are promising results, but they are not a promise that every app will get faster. The JIT is still experimental, so I would treat it as something to try and measure, not a production default.
Dropping Python 3.10
Python 3.10 has reached end of life. If your project supports every maintained Python release, you can now require Python 3.11. That removes some compatibility code and unlocks useful built-ins.
For example, reading TOML no longer needs a third-party parser:
import tomllib
config = tomllib.loads('name = "my-app"')Python 3.11 also added asyncio.TaskGroup, asyncio.timeout(), better tracebacks, and several SQLite improvements. They reduce the glue code you need in modern Python apps.
Upgrading with uv
If you use uv, the upgrade is short:
uv self updateuv python install 3.15uv python pin --global 3.15This installs 3.15 and makes it the default for uv run, unless a project selects another version.
Simon Willison’s original post is a good starting point if you want the full list of new features.