Yet another Python 3.12 release highlight summary
I'm doing a fair bit of Python lately, and thus need to stay up-to-date with the language releases. Some days ago Python 3.12 was released. The release note overview, and the detailed release notes in particular, are worth a read. Other articles I can recommend are What didn't make the headlines and Static typing improvements.
- PEP-701 formalisation of f-strings will lift some limitations, like nested f-strings, use of the quotation delimiter of the f-strings inside an expression, comments in multi-line f-strings. Examples are listed in the PEP.
>>> m = {'a': 'b'}
# This wasn't possible in prior versions.
>>> f'm={m['a']}'
'm=b'
Since Python3.8 there's also a debug format in f-strings, that is used when post-fixing a variable with =
:
>>> f'{m=}'
"m={'a': 'b'}"
# or as nested f-string
>>> f'Debug output {f'{m=}'}'
"Debug output m={'a': 'b'}"
- PEP 688 exposes the Buffer protocol (interface) to Python code. This was limited to CPython before. I'm not sure what this is useful for.
- PEP 669 introduces a new, low-overhead debugging and profiling API.
- PEP 684 adds support for isolated sub-interpreters with separate GILs, and is only available through the C-API. So, not of any particular use for me.
- Improved error messages.
- Large and small performance improvements, enhanced Linux
perf
support. - PEP 695 adds a new type annotation syntax.
More compact way to create generic functions and classes:
def max[T](args: Iterable[T]) -> T:
...
Type aliases can be generic:
type Point[T] = tuple[T, T]
- PEP 698 adds a
@override
decorator to indicate methods that should be overridden in a sub-class. - The
sqlite3
module received a command-line interface. This might be useful as a simple SQLite interface on systems that only have Python available.
# python -m sqlite3 [-h] [-v] [filename] [sql]
$ python -m sqlite3
sqlite3 shell, running on SQLite version 3.39.5
Connected to a transient in-memory database
Each command will be run using execute() on the cursor.
Type ".help" for more information; type ".quit" or CTRL-D to quit.
sqlite>
- The
uuid
package receive a CLI as well. Now there's an easy cross-platform way to create UUIDs!
# python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-n NAMESPACE] [-N NAME]
$ python -m uuid -u uuid4
6ebeb0e2-6257-4eba-a638-97a20a222bed
itertools.batched
will be very useful for splitting workloads into evenly sized chunks.
>>> from itertools import batched
>>> files = [f'{i}.txt' for i in range(1,11)]
>>> list(batched(files, 3))
[('1.txt', '2.txt', '3.txt'), ('4.txt', '5.txt', '6.txt'), ('7.txt', '8.txt', '9.txt'), ('10.txt',)]
- There's now
pathlib.Path
directory traversal functionpathlib.Path.walk
. os.path.splitroot
splits a path into a three tuple of(drive, root, tail)
wheredrive
is always empty on POSIX systems.
# POSIX
>>> os.path.splitroot(os.getcwd())
('', '/', 'Users/someuser')
# Windows
('C:', '\\', 'Users\\someuser\\AppData\\Local\\Programs\\Python\\Python312')
tempfile.NamedTemporaryFile
received a booleandelete_on_close
argument, which should be self-explanatory.tempfile.mkdtemp
always returns an absolute path now.- Module
unittest
received a--durations=N
flag to show theN
slowest test cases. - Inlined return values can be accessed in a debugger session, without adding a intermediate variable.
# module.py
def g():
return 21*2
def f():
breakpoint()
return g()
f()
Here's how to show the return value:
$ python -m module
> /private/var/folders/mc/3ssj7_c51dv76d6ll79s5l3w0000gn/T/tmp.xHdeQkAESD/module.py(6)f()
-> return g()
(Pdb) n
--Return--
> /private/var/folders/mc/3ssj7_c51dv76d6ll79s5l3w0000gn/T/tmp.xHdeQkAESD/module.py(6)f()->42
-> return g()
(Pdb) $_retval
42
(Pdb)