Python 3.11: Significant Speed Improvements

python dev

Python 3.11 is here, and it’s fast. The Faster CPython project, led by Microsoft-funded developers including Guido van Rossum, delivered real performance improvements. Here’s what changed.

The Numbers

Official benchmarks:

# Simple benchmark
python3.10 -m timeit "sum(range(1000000))"
# 10 loops, best of 5: 25.3 msec per loop

python3.11 -m timeit "sum(range(1000000))"
# 10 loops, best of 5: 17.8 msec per loop  (~30% faster)

What Made It Faster

1. Specializing Adaptive Interpreter

Python 3.11 watches bytecode execution and optimizes hot paths:

def add_numbers(a, b):
    return a + b

# First few calls: generic BINARY_OP
# After detecting int+int pattern: BINARY_OP_ADD_INT

The interpreter specializes operations based on observed types.

2. Cheaper Exceptions (Zero-Cost)

# Pre-3.11: try/except has overhead even when no exception
try:
    result = dict_lookup[key]  # Cost even if key exists
except KeyError:
    result = default

# 3.11: Zero-cost try blocks
# No overhead unless exception actually raised

Now the “Easier to Ask Forgiveness” pattern is genuinely free.

3. Faster Function Calls

Stack frames are cheaper:

# Recursive function benchmark
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

fib(30)  # Significantly faster in 3.11

~25% faster function call overhead.

4. Inlined Python Function Calls

# Simple functions may be inlined
def square(x):
    return x * x

result = square(5)  # May not create full stack frame

Better Error Messages

This isn’t performance, but it’s a game-changer:

Before (3.10)

d = {"a": {"b": None}}
print(d["a"]["b"]["c"])

# Traceback:
# TypeError: 'NoneType' object is not subscriptable

After (3.11)

d = {"a": {"b": None}}
print(d["a"]["b"]["c"])

# Traceback:
#   print(d["a"]["b"]["c"])
#         ~~~~~~~~^^^^^
# TypeError: 'NoneType' object is not subscriptable

The caret points to exactly which part failed.

Complex Expressions

x = [1, 2]
y = [3, 4]
print(x[0] + y[2] + x[1])

#   print(x[0] + y[2] + x[1])
#                ~~~~^^^
# IndexError: list index out of range

Debug time reduced significantly.

Exception Groups

New feature for handling multiple exceptions:

# Raise multiple exceptions together
def validate_data(data):
    errors = []
    if not data.get('name'):
        errors.append(ValueError("name required"))
    if not data.get('email'):
        errors.append(ValueError("email required"))
    
    if errors:
        raise ExceptionGroup("Validation failed", errors)

# Handle with except*
try:
    validate_data({})
except* ValueError as e:
    print(f"Got {len(e.exceptions)} ValueError(s)")
except* TypeError as e:
    print(f"Got TypeError(s)")

Particularly useful for async code with multiple failures.

TaskGroup for Asyncio

import asyncio

async def fetch_all():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(fetch_url("http://example.com"))
        task2 = tg.create_task(fetch_url("http://example.org"))
        task3 = tg.create_task(fetch_url("http://example.net"))
    
    # All tasks complete when context exits
    # Exceptions are collected as ExceptionGroup
    return [task1.result(), task2.result(), task3.result()]

Cleaner than asyncio.gather() with better error handling.

TOML Support

import tomllib  # New in 3.11

with open("pyproject.toml", "rb") as f:
    config = tomllib.load(f)

print(config["project"]["name"])

Finally, native TOML parsing. No more third-party packages for basic config reading.

Typing Improvements

Self Type

from typing import Self

class Builder:
    def set_name(self, name: str) -> Self:
        self.name = name
        return self
    
    def set_value(self, value: int) -> Self:
        self.value = value
        return self

# Correct type inference for subclasses too
class ExtendedBuilder(Builder):
    def set_extra(self, extra: str) -> Self:
        self.extra = extra
        return self

TypeVarTuple

from typing import TypeVarTuple

Ts = TypeVarTuple('Ts')

def concat(*args: *Ts) -> tuple[*Ts]:
    return args

# Type-safe variadic generics

Required and NotRequired

from typing import TypedDict, Required, NotRequired

class User(TypedDict, total=False):
    name: Required[str]
    email: Required[str]
    age: NotRequired[int]

Migration Notes

Compatibility

Most code runs unchanged. Some edge cases:

# object.__getstate__ behavior changed slightly

# Some C API changes affect extensions
# Update your Cython, NumPy, etc.

Check Your Dependencies

pip install --upgrade numpy pandas
# Ensure compatible versions for 3.11

Real-World Impact

Django

Reports of 10-25% faster request handling for typical views.

Data Processing

# Pandas operations
df = pd.read_csv("large.csv")
result = df.groupby("category").sum()
# 15-20% faster in 3.11

CLI Tools

Faster startup time means snappier command-line tools.

Should You Upgrade?

Yes, if:

Wait, if:

Final Thoughts

Python 3.11 delivers on the promise of making Python faster without breaking compatibility. The Faster CPython project continues—3.12 and beyond will bring more.

Upgrade when your dependencies are ready. The performance gains are real.


10-60% faster, and the error messages actually help.

All posts