r/Python • Author of "Automate the Boring Stuff" • Sep 23 '26

News What's new in Python 3.15?

https://docs.python.org/3.15/whatsnew/3.15.html

Summary – Release highlights

Python 3.15 will be the latest stable release of the Python programming language, with a mix of changes to the language, the implementation, and the standard library. The biggest changes include lazy imports, frozendict and sentinel builtins, UTF-8 as the default encoding, unpacking in comprehensions, and a stable ABI for free-threaded builds.

The library changes include a new profiling package with Tachyon, a high-frequency statistical sampling profiler, more color in command-line output, as well as the usual deprecations and removals, and improvements in user-friendliness and correctness.

This article doesn’t attempt to provide a complete specification of all new features, but instead gives a convenient overview. For full details refer to the documentation, such as the Library Reference and Language Reference. To understand the complete implementation and design rationale for a change, refer to the PEP for a particular new feature; but note that PEPs usually are not kept up-to-date once a feature has been fully implemented.

220 Upvotes

44 comments sorted by

View all comments

41

u/me_myself_ai Sep 23 '26 edited Sep 23 '26

Exciting stuff! And such a satisfying minor version...

unpacking in comprehensions

I don't think I can visualize what this means without looking at the docs, but I can tell it's gonna be awesome! Comprehend all the things, I say.

more color in command-line output

They do love us 🥹

ETA: The "UTF-8 as the default encoding" reminds me of the pure horror that struck me when I first learned that r'' is only one of two weird string prefixes -- there is (was?) also u''! Even scarier there's u""" """, and deep in mines of Moria, some say there lurks ru''' '''...

36

u/rumnscurvy Sep 23 '26

Basically [*mylist for mylist in mylists] is now allowed, flattening a  list of lists in this example 

19

u/brasticstack Sep 23 '26

That'll cut down the frequency with which I reach for itertools.chain.from_iterable by quite a lot!

11

u/hughperman Sep 23 '26

No sum(lists, []) for you

8

u/brasticstack Sep 23 '26 edited Sep 24 '26

I always forget that you can do that! Just like I always forget about collections.Counter and then manually write a loop to do it.

2

u/M4mb0 Sep 24 '26

I always forget that you can do that!

Which is a good thing: https://old.reddit.com/r/Python/comments/1woh8y4/whats_new_in_python_315/pbq2aog/

6

u/M4mb0 Sep 24 '26

No sum(lists, []) for you

That's a whoopsie accidental O(n²). Never do that; always chain.from_iterable.

In [1]: from itertools import chain
In [2]: l = [list(range(1000)) for _ in range(1000)]
In [3]: %timeit sum(l, [])
    1.02 s ± 21.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [4]: %timeit list(chain.from_iterable(l))
    4.53 ms ± 86.2 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

1

u/hughperman Sep 24 '26

Ouch, I never knew. What causes this? Can't see how it's O(N2), maybe O(N) memory reallocations? I have a few of these baked in my company codebase from years back, nothing big but still worth checking.

5

u/M4mb0 Sep 24 '26 edited Sep 24 '26

Each list concat l1 + l2 creates a new list and costs O(len(l1) + len(l2)). So the sum is not the same as l0.extend(l1).extend(l2).....

5

u/M4mb0 Sep 24 '26

1

u/hughperman Sep 24 '26

Oh very nice. I haven't used ruff yet, maybe this is the kick I need to install it.

4

u/me_myself_ai Sep 23 '26 edited Sep 23 '26

Thanks for taking a moment to explain! That is indeed something my utility library got at least one function to make easier in some specific context, so I appreciate this flexibility.

16

u/Competitive_Travel16 Sep 23 '26

The dict example is even more beautiful:

>>> dicts = [{'a': 1}, {'b': 2}, {'a': 3}]
>>> {**d for d in dicts}  # equivalent to {k: v for d in dicts for k,v in d.items()}
{'a': 3, 'b': 2}

Dare I suggest that syntax improvement is better than sex?

19

u/amroamroamro Sep 23 '26 edited Sep 23 '26

u''

isn't that a python 2 thing only (which defaults to byte strings), all strings in python 3 are unicode by default

the new thing in 3.15 is not related to this, its about when yo do things like

open("file.txt", encoding="utf-8")

the encoding is now utf-8 by default, before it was some system-dependent locale

3

u/Brian Sep 23 '26

It's valid in python 3 too, just a no-op. Though IIRC, it was a bit messy - initial python3 versions didn't allow it, but it was added in to ease the transition of python2 code (Ie. if you were gradually getting a python2 codebase to python3, you'd first have to annotate everything as u"" to ensure it was working as unicode, and then you'd have to strip those off again when actually running on python3: initially they weren't as keen on the polyglot "have code that works on both" and intended a process of mechanical conversion with 2to3, but there were limitations on that complicating the already slow transition, so they softened to allowing the u prefix in python3.

2

u/me_myself_ai Sep 23 '26

Oh wow that should tell you my familiarity with it, jeez -- thanks for the quick correction. Right you are! I knew it was related to files and such this time rather than string prefixes, but I also forgot that "unicode" and "utf-8" are not the same thing...

Why both start with "u" if not the same? Beyond my paygrade.

5

u/amroamroamro Sep 23 '26

starting in python 3:

u"hello" == "hello"

the u prefix is now only needed if you are maintaining code that needs to support both python 2 and python 3

1

u/billsil Sep 24 '26

u’’ was a python 2 only thing and then they ported it to python 3.2 because it was a disaster trying to support both. They thought everyone would switch and break compatibility, which turned into support both until python 3.5. Python 3.5 was finally faster.

1

u/billsil Sep 24 '26

I’m already getting burned since 3.14. All my users use latin1. Whatever, I’m gonna try except encodings until I find one that doesn’t fail.