Back to blog

Python 3.15 Brings Lazy Imports and JIT Compiler: What Changes for Developers

Hello HaWkers, Python continues its impressive evolution. Version 3.15, currently in alpha phase, brings changes that can significantly transform performance and the development experience. Lazy imports, a more mature JIT compiler, and UTF-8 as default are just some of the new features.

Let's explore what's coming and how you can start testing these features.

Lazy Imports: On-Demand Loading (PEP 810)

One of the most anticipated additions in Python 3.15 is lazy imports, defined in PEP 810. This feature allows modules to be loaded only when actually used, drastically reducing application startup time.

The Problem Lazy Imports Solve

Currently, when you import a module in Python, it's completely loaded into memory, even if you only use a small part of it:

# Current behavior (Python <= 3.14)
import pandas as pd  # Loads ALL of pandas immediately
import numpy as np   # Loads ALL of numpy immediately
import tensorflow as tf  # Can take several seconds!

# Even if you only use one function...
result = pd.read_csv('data.csv')

In projects with many dependencies, this can result in startup times of 10+ seconds.

How Lazy Imports Work

With Python 3.15, you can use lazy imports that only load when needed:

# Python 3.15+ with lazy imports

# Method 1: Using the new lazyimport module
from lazyimport import lazy_import

pd = lazy_import('pandas')
np = lazy_import('numpy')
tf = lazy_import('tensorflow')

# At this point, NO module has actually been loaded!
print("Application started")  # Executes instantly

# Only now is pandas loaded
df = pd.read_csv('data.csv')
# Method 2: Using the __future__ import
from __future__ import lazy_imports

import pandas as pd  # Still doesn't load!
import numpy as np   # Still doesn't load!

# Loading happens on first use
result = pd.DataFrame({'a': [1, 2, 3]})

Practical Benefits

Startup time:

  • CLI applications: from 5-10s to < 1s
  • Simple scripts: from 2-3s to < 0.5s
  • Web services: much faster cold start

Memory usage:

  • Unused modules don't occupy RAM
  • Ideal for CLIs with many commands
  • Better for serverless/FaaS

JIT Compiler: Native Performance

Python 3.15 brings significant improvements to the experimental JIT (Just-In-Time) compiler that was introduced in Python 3.13.

Real Performance Gains

Benchmarks show consistent gains of 20% or more for specific workloads:

# Code that benefits from JIT
def calculate_fibonacci(n: int) -> int:
    """Intensive calculation that JIT optimizes well."""
    if n <= 1:
        return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

# With JIT enabled, this type of loop
# can be 20% faster
result = calculate_fibonacci(1000000)

Enabling JIT

# Run Python with JIT enabled
python -X jit script.py

# Or via environment variable
PYTHON_JIT=1 python script.py

When JIT Helps (and When It Doesn't)

Scenarios with significant gains:

  • Intensive numeric loops
  • Data processing in pure Python
  • Mathematical algorithms
  • Text parsing and transformation

Scenarios with little impact:

  • Code already using NumPy/Pandas (already optimized in C)
  • I/O bound (waiting for network/disk)
  • Typical web applications

UTF-8 as Default (PEP 686)

Finally! Python 3.15 adopts UTF-8 as the default encoding for I/O operations.

The Previous Problem

# Python <= 3.14 on Windows
# Could use cp1252 or another system encoding

with open('file.txt') as f:
    text = f.read()  # Encoding depended on the system!

# Different result on Windows vs Linux vs macOS

The New Behavior

# Python 3.15+
# UTF-8 is ALWAYS the default, on any system

with open('file.txt') as f:
    text = f.read()  # Always UTF-8!

# Consistent on Windows, Linux, macOS
# No longer need to specify encoding='utf-8'

Safe Migration

If your code depended on system encoding, you can keep the old behavior:

# Keep old behavior if necessary
import locale

# Get system encoding
system_encoding = locale.getpreferredencoding(False)

with open('file.txt', encoding=system_encoding) as f:
    text = f.read()

Template Strings (t-strings) - PEP 750

A new way to create processable strings:

# Python 3.15+ t-strings

from string import Template

# t-strings work like f-strings but are processable
name = "Maria"
age = 25

# Traditional f-string (result is final string)
message_f = f"Hello, {name}! You are {age} years old."

# t-string (result is processable Template object)
message_t = t"Hello, {name}! You are {age} years old."

# You can inspect and modify before rendering
print(message_t.args)  # {'name': 'Maria', 'age': 25}
print(message_t.template)  # "Hello, {name}! You are {age} years old."

Use Cases

Security in SQL/HTML:

# t-strings allow automatic sanitization

def html_escape(template):
    """Escapes dangerous values automatically."""
    from html import escape
    args = {k: escape(str(v)) for k, v in template.args.items()}
    return template.template.format(**args)

user_input = "<script>alert('xss')</script>"
safe_html = html_escape(t"<div>{user_input}</div>")
# Result: <div>&lt;script&gt;alert('xss')&lt;/script&gt;</div>

Free-Threaded Python (PEP 779)

Python 3.15 marks the version where free-threaded mode (no GIL) is officially supported:

# Python 3.15+ with free-threading

import threading

# Now Python threads can truly execute in parallel!
def heavy_work(n):
    total = 0
    for i in range(n):
        total += i * i
    return total

# Create threads that actually execute in parallel
threads = []
for _ in range(4):
    t = threading.Thread(target=heavy_work, args=(10_000_000,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

# In Python 3.15 free-threaded, this uses 4 cores!

How to Enable

# Install Python with free-threaded support
# (special build required)

# Check if enabled
python -c "import sys; print(sys.flags.nogil)"

New High-Performance Profiler (PEP 799)

Python 3.15 includes a new low-overhead profiler:

# Using the new profiler
import _profiler

# Low-impact statistical profiler
with _profiler.profile() as p:
    # Your code here
    result = complex_function()

# Analyze results
stats = p.get_stats()
for func, time in stats.most_common(10):
    print(f"{func}: {time:.2f}ms")

Advantages Over cProfile

Comparison:

  • cProfile: 30-50% overhead
  • New profiler: < 5% overhead
  • Ideal for production profiling

Native Zstandard Compression (PEP 784)

Native support for Zstandard, a modern compression algorithm:

# Python 3.15+
import compression.zstd as zstd

# Compress data
data = b"data to compress" * 1000
compressed = zstd.compress(data)

print(f"Original: {len(data)} bytes")
print(f"Compressed: {len(compressed)} bytes")
print(f"Ratio: {len(compressed) / len(data):.2%}")

# Decompress
original = zstd.decompress(compressed)
assert original == data

Why Zstandard?

Algorithm comparison:

  • gzip: good compression, medium speed
  • lz4: ok compression, very fast
  • zstd: great compression, very fast (best of both worlds)

When Will Python 3.15 Be Released

Official Timeline

Alpha Phase (current):

  • 3.15.0a1: October 2025
  • 3.15.0a3: December 2025 (current)
  • 3.15.0a7: April 2026

Beta Phase:

  • 3.15.0b1: May 2026
  • Features frozen

Release Candidate:

  • 3.15.0rc1: July 2026

Final Release:

  • 3.15.0: October 2026

Conclusion

Python 3.15 represents a significant evolution of the language, with focus on performance and developer experience. Lazy imports and the mature JIT compiler may finally address historical complaints about Python's speed.

If you work with Python, it's worth following these changes and starting to test alpha versions in experimental projects.

If you're interested in performance in programming languages, I recommend checking out another article: Microsoft Wants to Eliminate All C and C++ Code by 2030 Using Rust and AI where you'll discover how big companies are rethinking their language choices.

Let's go! 🦅

Comments (0)

This article has no comments yet 😢. Be the first! 🚀🦅

Add comments