Python 3.9 is out (and has been for a while, I’m late posting this), with new features and changes. The themes for this release have been heavily internal improvements, such as a new more powerful parser, and the usual static typing improvements, along with a several bits new-user facing new syntax. This makes 3.9 a smaller release, but still it has some nice features of note.
Dict merging
This is a small feature, but one that has been looked for by Python newbies and experts alike. You can now use set merging on dicts, as well!
dict_1 = {"a": 1, "b": 2}
dict_2 = {"b": 3, "c": 4}
dict_12 = dict_1 | dict_2
This feature was a little slow in coming partially because there’s a choice to
be made; in the example above, does dict_12["b"]
contain 2 or 3, a mixture of
both, or does the expression throw an error? The answer selected by Python is
3
; just like with unpacking dict_12 = {**dict_1, **dict_2}
, the final item
overrides. This syntax works with dict-like classes too, unlike dict unpacking,
which always returns a dict, regardless of whether you start with something else
like an OrderedDict
.
Generics for type hinting
The standard library classes now can be generic for static typing. So
list[int]
is now valid at runtime, though it does nothing. You an now write
either of the following lines:
xs: list[int] = []
xs = list[int]()
These are identical (though the first line is also valid in Python 3.7+ if you
use from __future__ import annotations
), and are not checked or enforced at
runtime. This avoids one of the reasons to import typing
in a simple script
for static typing (pipe operators for types in 3.10 eliminates even more).
This is enabled by __class_getitem__
, which was added in Python 3.7 to speed
up and simplify the typing module and classes.
Also for typing, typing.Annotated
was added to provide a way to add non-typing
information to a type.
Smaller features
A new parser was implemented, though since the old parser is still optionally
available, the language doesn’t officially use any of the new syntax. Though
if you try a parenthesised with
statement, it will work as long as you use the
default new parser.
Decorators now have relaxed syntax; pretty much any Python object can be used directly now, instead of just simple dotted expressions.
New string methods .removeprefix
and .removesuffix
were added, partially
because they are sometimes useful, and partially because new users expect
.lstrip
/.rstrip
to have this behavior.
Also, __file__
now is always absolute in your __main__
module.
Stdlib
Time zones
A new zoneinfo
library was dded with timezone information. Having time zone
info is great for properly handling times.
Topological sorting
A new module graphlib
was added with a single topological sorter. This is
useful when you have tasks that depend on other tasks.
GRAPH = {
"numpy": set(),
"matplotlib": {"numpy"},
"pandas": {"numpy", "matplotlib"},
"requests": set(),
"uncertainties": {"numpy"},
"scipy": {"numpy"},
}
ts = TopologicalSorter(GRAPH)
ts.prepare()
print(*ts.static_order())
You can get an order like you see above, or you can get “ready” ones and then
indicate which ones are done, checking to see which ones are ready - perfect for
multithreading. See is_active()
, get_ready()
, and done(value)
.
Other
ast.unparse()
added as the reverse of.parse()
. Also.dump()
can take anindent=
value.asyncio
received several updates, including.to_thread()
, which is handy way to integrate with classic threading.cancel_futures=True
option added toconcurrent.futures
.sys.platlibdir
added, usually"lib"
or"lib64"
.- Several new
math
functions:lcm()
,nextafter()
, andulp()
. importlib.metadata.files()
addedos.unsetenv()
available on Windows - makingputenv
andsetenv
always available.
Other developer changes
Library developers may need to be aware of the following changes:
- Several optimizations
- Some deprecations & removals of previously deprecated things
- Some new C API, like
PyType_*
,PyFrame_*
, andPyThreadState_*
additions, alsoPyModule_AddType()
. - Fast
PyObject_CallNoArgs()
andPyObject_CallOneArg()
functions.
Final words
This was an unusually small release from a user perspective, but there was a lot of work going on behind the scenes, especially related to the brand new and more powerful parser. This was the first release to officially support Apple Silicon.
Sources
This blog post was written well after the release, so the primary source was the docs & experience. Here are a few possibly useful links: