# f-string: eagerly evaluated, returns str
name = "world"
result = f"Hello {name}"
print(result, type(result))Hello world <class 'str'>
__future__: Where is Python Going?We’ve seen a few trends in Python over the past 5+ years of releases:
We’ll take a look at a few of the remaining changes that will likely affect how Python works in the future, as well as what is coming in the next two versions of Python.
Python 3.14 - Oct 2025
Like f-strings but with control over how values are interpolated into the template string:
# f-string: eagerly evaluated, returns str
name = "world"
result = f"Hello {name}"
print(result, type(result))Hello world <class 'str'>
# t-string: lazily evaluated, returns Template
name = "world"
result = f"Hello {name}"
print(result, type(result))Hello world <class 'str'>
import html
from string.templatelib import Template, Interpolation
def safe_html(template: Template) -> str:
parts = []
for item in template:
if isinstance(item, str):
parts.append(item)
elif isinstance(item, Interpolation):
# differentiates programmer-provided string from
# interpolated values, allowing context-specific escaping
parts.append(html.escape(str(item.value)))
return "".join(parts)
user_input = "<script>alert('xss')</script>"
safe_html(t"<p>Hello {user_input}</p>")'<p>Hello <script>alert('xss')</script></p>'
Introduced in 3.14, needs to be enabled with environment variable PYTHON_JIT=1, accessed via sys._jit.
At the moment– no real performance gains (slower in many cases); considered a foundational release just like free-threading.
Notable difference: Does not require a different install, can be enabled/disabled with environment variable (must be set on Python start-up).
This along with free-threaded Python are laying the foundation for the next decade of Python. Let’s look at some of the features coming in Python 3.15:
# import doesn't execute until name is used
lazy import numpy as np # not loaded yet
lazy import pandas as pd # not loaded yet
def analyze(data):
return np.array(data) # numpy loads here, on first use# moduleA.py
class TypeA:
pass
from moduleB import some_func# moduleB.py
from moduleA import TypeA # circular import!
def some_func() -> TypeA:
# needs to be imported here though
return TypeA(...)Often instead written:
# moduleB.py
# as of Python 3.14, TypeA is lazy, no more need for
# from __future__ import annotations
def some_func() -> TypeA:
from moduleA import TypeA # this is OK since it only runs after moduleB is fully defined
return TypeA(...)But now can be written:
# not actually fully imported until *used*
lazy from moduleA import TypeA
def some_func() -> TypeA:
return TypeA(...)abi3t — stable ABI for free-threaded builds. Means C extensions can ship one wheel that works across 3.15+ free-threaded builds. Huge for ecosystem adoption.
frozendict| Mutable | Immutable |
|---|---|
list |
tuple |
set |
frozenset |
dict |
? |
bytearray |
bytes/str |
frozendict fills this gap, an immutable dictionary type that is hashable if all types inside are hashable– it can be used in a set or as a dictionary key.
frozendict(a=3, b=4)profiling.samplingDemo: https://docs.python.org/3.15/library/profiling.sampling.html
typing aliasesFor a while the correct way to type builtins was typing.List[str], typing.Dict[int, int]– now that it is possible to just use list[str], dict[int, int], etc. these old aliases will be removed.
This is one of the final steps in stabilizing Python’s typing interface. After about 6 years of improvements it is likely that things will quiet down significantly.
Introduced without a PEP in 3.14, removed in 3.15 for regressions– it is likely that in 3.16 a more formal PEP that addresses the issues will be attempted. It is unclear if this will land in 3.16 or 3.17 but it’ll be a focus until something performant and stable lands.
Moving from a purely-interpreted language to a JIT will likely take a few years. 3.15 is said to be 5-10% faster, but there are lots of gains to be explored here.
With the stabilized ABI in 3.15, eventual re-introduction of the improved garbage collector, and a few versions for the ecosystem to catch up– it seems likely that by 3.17 or so the permanently removing the GIL is likely to finally happen.
match is still new, and most Python features evolve for a few years once landing. Looking to languages like Rust, there is a lot that could be done to make it more robust. As the feature grows in use, demand for better ergonomics will likely come to pass.
There will be increased demand for a ‘compiled Python’– one of the last major pain points left unaddressed if the GIL is gone. The ability to run a program like pythonc and get a binary is something that there have been many attempts at and a place where Go/Rust have a much nicer dev experience.
A more comprehensive package manager like uv being made official– pip is aged and incomplete. uv has the developer mindshare that poetry, pdm, and other tools never achieved.
Perhaps an official type checker, the current mypy vs. pyright vs. ty setup is fine for experimentation, but with a stable typing system the question of why isn’t this part of Python gets harder to answer.
Similarly, a pyfmt or similar based on ruff? Go and Rust have equivalents built-in. With LLM-generated code this seems like an obvious need.