What's new in Python 3.9?
New `str` methods, better typing support, new modules, and more.
- By Tushar
- ·
- Insights
- Python
The latest Python version has become increasingly common among users since its addition to Ubuntu 21.04 a couple of months ago. That gives developers plenty of reason to start using it in their projects right away. So we think it's fair for you to know what features the newest release has to offer — and you're definitely going to like these!
New string methods str.removeprefix
and str.removesuffix
This is going to be a fan favorite feature, as it solves a rather confusing mix-up in Python's old str
methods.
Suppose you want to remove the .py
extension from a file name, how'd you do that? You'd think you could of course use the str.rstrip
function to strip out the extension from the right end of the string, and you'd be right:
>>> file_string = 'my_test_file.py'
>>> file_name = file_string.rstrip('.py')
>>> file_name
'my_test_file'
... except this has a fatal issue. See if you can find the problem with this output:
>>> file_string = 'make_happy.py'
>>> file_name = file_string.rstrip('.py')
>>> file_name
'make_ha'
🤨 why did it delete part of the file name?
Well, it's because rstrip
(as well as lstrip
and strip
) doesn't take a "string", but a set of characters to strip. Therefore, file_string.rstrip('.py')
doesn't mean strip out .py
, it means to strip out any of these characters: .
, p
, and y
, which is why the ppy
at the end of our filename go stripped as well. And exactly this has caused bugs and confusion among many other Pythonistas.
And this is why Python 3.9 adds two new methods: str.removeprefix
and str.removesuffix
which does treat the given string as a string:
>>> file_string = 'make_happy.py'
>>> file_name = file_string.removesuffix('.py')
>>> file_name
'make_happy'
New dict
merge and update syntax
Python dictionaries can already be merged and updated, like so:
>>> alice = {'apples': 1, 'bananas': 2}
>>> bob = {'carrots': 3}
>>> merged = {**alice, **bob} # Dictionary unpacking added in Python 3.5
>>> merged
{'apples': 1, 'bananas': 2, 'carrots': 3}
>>> alice
{'apples': 1, 'bananas': 2}
>>> alice.update(bob) # Updates alice in-place with bob's values
>>> alice
{'apples': 1, 'bananas': 2, 'carrots': 3}
Since merging and updation are very commonly used features of dictionaries (just like sets), this new addition simply makes these actions simpler, with the |
operator:
>>> alice = {'apples': 1, 'bananas': 2}
>>> bob = {'carrots': 3}
>>> merged = alice | bob # Merges the two into a new dictionary
>>> merged
{'apples': 1, 'bananas': 2, 'carrots': 3}
>>> alice
{'apples': 1, 'bananas': 2}
>>> alice |= bob # Updates alice in-place
>>> alice
{'apples': 1, 'bananas': 2, 'carrots': 3}
>>>
Type hinting generics in standard collections
I personally am a huge fan of this addition — and if you use type annotations, you'll be a fan of this too. Starting in Python 3.9, you can start using all built-in collection types: list
, dict
, set
, collections.deque
etc. in your type annotations, instead of typing.List
, typing.Deque
, etc. No more typing
imports needed!
# Previously:
from collections import defaultdict
from typing import DefaultDict, List, Set
values: List[int] = []
words: Set[str] = set()
counts: DefaultDict[int, int] = defaultdict(int)
# Starting from Python 3.9:
from collections import defaultdict
values: list[int] = []
words: set[str] = set()
counts: defaultdict[int, int] = defaultdict(int)
More information is available in PEP 585.
Two new modules
Python 3.9 introduces two new modules:
zoneinfo
which brings support for the IANA time zone database to the standard library. What it means is that Python now has built-in support for things like daylight savings, time shifts in countries throughout the year, etc.graphlib
, which has an implementation of the topological sort algorithm. So you now have a built-in method for solving complex graph problems, like sorting dependencies in a build system.
CPython has a new, more powerful parser
CPython now uses a new parser based on PEG
, instead of the old LL1
algorithm based parser. What this means, is that there were certain limitations to what kind of syntax could be added to Python, as the older parser wouldn't be able to read the more complex code at all.
An example of this is a multi-line with
statement:
# Previously:
with open('file1.txt') as f1, \
open('file2.txt') as f2:
print(f1.read())
print(f2.read())
Normally you would use parentheses around a multiline statement to have a more readable line grouping, and to avoid having to put trailing \
-es at the end of your lines.
But the older parser in Python 3.8 did not support this syntax:
# Starting from Python 3.9:
with (
open('file1.txt') as f1,
open('file2.txt') as f2,
):
print(f1.read())
print(f2.read())
The new and improved PEG
parser algorithm will be able to handle much more complex syntax parsing, if needed in the future.
More information available in PEP 617.
Other additions
- CPython adopts an annual release cycle, i.e. a new minor version of CPython will be released every year.
- New
random.Random.randbytes
method to generate random bytes. - The
__file__
built-in variable is always an absolute path now. sys.stderr
is always line buffered starting from Python 3.9.ast
module updates:- New
ast.unparse
method to convert an AST back to code. - Added
indent
option toast.dump()
for indentation, similar tojson.dumps
.
- New
- PEP 614, relaxed grammar restrictions on decorators.
Summary
This version of Python has brought us many convenient additions, while also setting the groundwork for much bigger features down the line with the updates to the parser system and typing changes. For a more detailed look into the changes in this release, you can check out the docs.