My Favorite New Python 3.15 Language Features

Python 3.15 is scheduled for release on October 1st, 2026.1 With its release approaching, I performed the usual routine of looking at what new features the new version has to offer. So why not share my favorites here?

You can check all the new features here.

1. Explicit lazy imports (PEP 810)

You can now write lazy import foo or lazy from foo import Bar and the module will be loaded only when used. This is to avoid the practice of importing a library in the body of a function or class. I used this pattern a lot at work in order to avoid loading libraries with big loading times when not needed. However, I never liked to obscure dependencies inside some deep function. I like my imports to be clear and visible up front. Finally, now I don’t need to worry about this anymore.

2. A builtin sentinel() type (PEP 661)

You can now use sentinel() to create a sentinel, replacing the MISSING = object() idiom.

I have not used this pattern a lot, but it happened. If you don’t know it, it is a way to differentiate between a missing value and an explicit None.

Imagine that you are creating a function that updates a user like the following:

1
2
3
def update_user(name=None):
    if name is None:
        ...

Once you reach the inside of that if block, you have a problem. Is name None because the user didn’t provide a value, or because the user explicitly passed None?

In practice, update_user() and update_user(name=None) are virtually indistinguishable. The solution, so far, was to use a “hack” like the following:

1
2
3
4
5
6
7
8
9
MISSING = object()

def update_user(name=MISSING):
    if name is MISSING:
        print("No name was provided")
    elif name is None:
        print("Name was explicitly set to None")
    else:
        print(f"New name: {name}")

But using a plain object() comes with some annoyances. Now, you can use sentinel() and, I suppose, everything will be more clear.

3. Unpacking inside comprehension (PEP 798)

This is a small but handy language feature. It will now be possible to write [*xs for xs in things] instead of [x for xs in things for x in xs] to “flatten” a list of lists. The same is true for dict types as well.

I know it is not much, but I always mix up the order of for xs in things for x in xs, so I guess this will save me some mental overhead.

4. frozendict is now a built-in type (PEP 814)

As the title says, finally frozendict is a built-in type!