May 21, 2026
I Didn’t Expect Python 3.15 to Change This Much
This Might Be the Biggest Upgrade in Years
Aysha R
6 min read
Every Python release claims to be faster, cleaner, smarter, more ergonomic, whatever.
Most of the time, the changes are either extremely niche or technically impressive but irrelevant to how people actually write Python.
Python 3.15 feels different.
Not because every feature is revolutionary. Some of them are honestly overdue. Some feel like cleanup work after previous experiments. And a few are the kind of thing only language nerds will care about.
But this is one of the first Python releases in a while where multiple changes immediately affect normal development work.
Lazy imports alone will change how a lot of large Python applications start up.
And the funny thing is, Python 3.15 also includes a rollback of one of Python 3.14's biggest changes.
Which honestly says a lot about Python development culture. They shipped the incremental garbage collector, people complained about memory usage, and now it's gone again.
Good.
That's healthier than pretending every experimental runtime change was secretly brilliant.
Lazy imports should have existed years ago
This is probably the most practical feature in the release.
Python startup time has always had this weird problem where importing half the ecosystem feels like paying taxes before your code even runs.
You import one framework. That framework imports six utility modules. Those import twenty more. Then your CLI tool takes two seconds to print --help.
People worked around this manually for years
if expensive_feature_enabled:
import pandasif expensive_feature_enabled:
import pandasOr
def do_something():
import torchdef do_something():
import torchNot because the code looked good. Because startup latency mattered.
Now Python officially supports lazy imports.
Modules can be loaded only when actually used instead of during startup. And importantly, existing imports can often become lazy without rewriting the codebase into some cursed dependency maze.
That matters more than the syntax itself.
The real value here is for
- CLI tools
- developer tooling
- large web applications
- AI stacks with absurd import trees
- basically every Python project that accidentally became an operating system
There's also surprisingly little downside if your code already behaves properly.
Although let's be honest: some projects are going to discover they relied on import side effects more than they thought.
Python developers love pretending imports are just declarations. A lot of imports are actually initialization scripts wearing fake glasses.
The JIT is finally becoming visible in benchmarks
Python introduced its JIT in 3.13.
The reaction was mostly
"Okay. Cool. Anyway."
Because the speedups were small and inconsistent.
That wasn't necessarily a failure. The first versions were clearly infrastructure work. But people hear "JIT compiler" and expect something dramatic.
Python 3.15 is the first version where the improvements start looking real.
The reported gains are around 8%–13% geometric mean improvements depending on workload and platform.
No, that does not suddenly turn Python into Rust.
But an automatic 10% speed increase in a language this widely used is not small either.
Especially because most Python performance work normally involves
- rewriting hot paths in C
- vectorizing everything into NumPy
- adding caching layers everywhere
- pretending asyncio solved architectural problems
If CPython can continue making ordinary Python faster without requiring ecosystem rewrites, that matters.
The interesting part is that the JIT improvements are not one single trick. It's a collection of runtime optimizations
- tracing improvements
- better machine code generation
- register allocation
- reference counting optimizations
Basically the kind of low-level engineering most Python users never think about unless they accidentally open CPython internals.
Will developers notice immediately? Depends on the workload.
But unlike earlier versions, this no longer feels purely theoretical.
frozendict is one of those additions people argued about forever
Python almost never adds new built-in data structures.
So when it does, it usually means the ecosystem has already recreated the same thing badly a thousand times.
That's basically the story of frozendict.
Immutable dictionaries already existed in libraries. People hacked around them with tuples. People wrapped dictionaries. People used custom classes.
Now there's finally an official version.
config = frozendict({"host": "localhost", "port": 5432})config = frozendict({"host": "localhost", "port": 5432})You can't modify it. It's hashable. It can safely be used as a dictionary key.
Simple.
Honestly this feels less like a flashy feature and more like Python finally admitting reality.
sentinel() fixes a surprisingly annoying pattern
This is one of those tiny features that sounds unimportant until you've written enough Python.
A lot of codebases do this
MISSING = object()MISSING = object()Why? Because None might be a legitimate value.
So developers create anonymous unique objects as placeholders.
The problem is those objects are ugly, not self documenting, awkward for type checking, and generally feel like a workaround.
Now there's an official API
NOT_SET = sentinel("NOT_SET")NOT_SET = sentinel("NOT_SET")It's clearer. It prints properly. It works better with typing.
This is exactly the kind of language improvement Python is usually good at.
Not revolutionary. Just reducing unnecessary weirdness.
The new profiler is probably more important than people realize
Python profiling has historically had a trade off
You either get precise profiling with major overhead, or lightweight monitoring with less detail.
cProfile tracked basically everything. Which also meant it slowed programs down heavily.
Now Python 3.15 adds a statistical sampling profiler.
That's a much better fit for production ish performance analysis.
Instead of tracing every call, it periodically samples execution and estimates where time is spent.
Less precise. Far cheaper. Usually good enough.
Honestly, this is how profiling works in a lot of modern performance tooling already. Python was just oddly behind here.
And no, deterministic profilers are not going away. They still matter for certain debugging scenarios.
But for normal optimization work, sampling profilers are often the more realistic option.
Especially when developers already hesitate to profile because profiling itself changes runtime behavior.
Error messages continue getting less terrible
Python's error messages have improved a lot over the past few releases.
This sounds minor until you compare modern Python errors with older versions that basically just shrugged at you.
Python 3.15 improves suggestions further.
One funny example
my_list.push(1)my_list.push(1)Python now recognizes this is probably JavaScript brain damage and suggests
append()append()That's actually useful.
And honestly, modern programming involves enough language-switching that these mistakes are normal.
The attribute suggestions also got smarter in general.
Not groundbreaking. But better developer ergonomics accumulates over time.
People underestimate how much energy gets wasted on stupid debugging friction.
Unpacking inside comprehensions looks small but changes readability a lot
This feature will probably split people into two groups
- "finally"
- "this makes comprehensions unreadable"
Both reactions are fair.
Previously flattening nested iterables inside comprehensions looked like this:
[a for b in x for a in b][a for b in x for a in b]Which is technically fine. But not exactly pleasant.
Now
[*a for a in x][*a for a in x]That's easier to scan.
Dictionary unpacking also works
{**d for d in dicts}{**d for d in dicts}The feature itself is straightforward.
The bigger issue is that Python comprehensions already have a habit of turning into competitive programming puzzles if people get too clever.
So this is probably one of those features that is great in moderation and terrible in the hands of developers who think one-liners are a personality trait.
The garbage collector rollback is actually a good sign
This might be the most interesting part of the release philosophically.
Python 3.14 introduced an incremental garbage collector intended to reduce pause times.
Reasonable goal.
Then users started reporting increased memory usage. Sometimes dramatically increased.
Python 3.15 rolls the change back.
And honestly, that's fine.
A runtime optimization that causes memory bloat is not automatically worth keeping just because the implementation was complicated.
There's a tendency in some ecosystems to defend every performance experiment forever because reversing course looks embarrassing.
Python backing out the change instead of pretending users imagined the problem is healthier.
The old generational garbage collector returns for now. The incremental collector may come back later after more work.
That's probably the correct outcome.
Type system improvements keep expanding Python's split personality
Python typing continues evolving into its own parallel universe.
Some developers love this. Some tolerate it. Some still treat type hints as decorative comments.
Python 3.15 adds more TypedDict controls like
closedextra_items
Which make dictionary schemas stricter and more expressive.
There's also TypeForm, which helps represent evaluated type expressions.
Useful? Yes.
Exciting? Depends how much time you spend fighting static analysis tools.
The reality is that Python now serves two very different audiences simultaneously
- people who want flexible scripting
- people building massive typed systems at scale
Sometimes the language handles that tension well. Sometimes it feels like two languages sharing syntax.
Python 3.15 feels unusually practical
That's probably the main thing.
A lot of releases have impressive internal work that ordinary developers barely notice.
Python 3.15 includes several changes that directly affect
- startup performance
- profiling
- runtime speed
- error debugging
- data modeling
- code readability
Not every feature matters equally. Some are niche. Some will create arguments on Reddit for six months.
But this release feels grounded in actual developer behavior instead of language theory.
People complained about startup times. Python added lazy imports. People wanted immutable dictionaries. Python added one. People reported GC memory problems. Python reverted the change.
That feedback loop is probably the most reassuring thing here.
I still think some Python applications are becoming absurdly overengineered. The AI ecosystem especially seems determined to import the entire planet before printing a single token.
But at least CPython itself seems increasingly focused on practical pain points instead of pretending developer ergonomics and runtime behavior are separate topics.
We'll see how much of this survives contact with real-world projects once 3.15 ships fully.
Some features will quietly become normal. Some will be abused immediately.
Some will probably disappear in future releases.
Which is also very Python.