Every time Python comes out with a new version, I like to jump right in and start using the latest features—especially if they make my life easier. Well, Python 3.9 did not disappoint, and there are three new features I want to talk about today: the union operator, type hinting generics, and a pair of new string methods. Let’s dive in!
Table of Contents
Python 3.9 Overview
I wouldn’t exactly consider myself obsessed with Python, but I really love the language and everything the dev team does to keep it feeling fresh and new. In this latest installment of the language as of October 5th, 2020, we’re graced with 7 brand new features according to PEP 596:
- PEP 584, Add Union Operators To dict
- PEP 585, Type Hinting Generics In Standard Collections
- PEP 593, Flexible function and variable annotations
- PEP 614, Relaxing Grammar Restrictions On Decorators
- PEP 615, Support for the IANA Time Zone Database in the Standard Library
- PEP 616, String methods to remove prefixes and suffixes
- PEP 617, New PEG parser for CPython
While these are all great features, I’m obviously very biased in that I’m most excited by any feature that simplifies a task for me. After all, that’s what drew me into Python in the first place: I want to be able to move from concept to code as quickly as possible. Well, this latest round of updates didn’t disappoint! Let’s talk about it.
Python 3.9 Features
As a quick heads up, we’re going to be talking about three features from the list above:
- The Dictionary Union Operator
- Type Hinting Generics in Standard Collections
- String Methods to Remove Prefixes and Suffixes
While I’m sure all seven features are excellent, these three really caught my eye, so I wanted to focus on them. If you’re interested in hearing my opinion on the other four features, let me know!
Dictionary Union Operator
As many of you know, the one The Renegade Coder series that really brings in the views is my How to Python series. In it, I write about everyday problems and how to solve them. If you’re looking for a good place to start, here’s a nice list post.
At any rate, back in the summer of 2019, I wrote about how annoying it was to merge dictionaries. At the time, these were the best solutions:
yusuke_power = {"Yusuke Urameshi": "Spirit Gun"} hiei_power = {"Hiei": "Jagan Eye"} # Dictionary Comprehension powers = {key: value for d in (yusuke_power, hiei_power) for key, value in d.items()} # Dictionary unpacking (Python 3.5+) powers = {**yusuke_power, **hiei_power}
All of this just so we can get the following dictionary:
powers = { "Yusuke Urameshi": "Spirit Gun", "Hiei": "Jagan Eye" }
Oddly enough, this article gets cited quite a bit in the community (e.g. in this MiniScript forum) because it shows a major gap in language design. Surely, merging dictionaries should be easier.
Well, in late 2020, we finally have an option: the dictionary union operator. It’s a simple change, but basically it allows us to reduce the code from above down to something like this:
yusuke_power = {"Yusuke Urameshi": "Spirit Gun"} hiei_power = {"Hiei": "Jagan Eye"} powers = yusuke_power | hiei_power
In this case, the new operator (|
) is doing all the heavy lifting. But wait, it gets better! If you want to merge dictionaries directly without generating a new dictionary, you can use the augmented union operator:
yusuke_power = {"Yusuke Urameshi": "Spirit Gun"} hiei_power = {"Hiei": "Jagan Eye"} yusuke_power |= hiei_power
In this case, yusuke_power
would store the result of the union. How cool is that?
As you can probably imagine, I updated my dictionary merging article a few months back when I heard about this feature. I can’t tell you how excited I am to see it in the official language.
Type Hinting Generics In Standard Collections
While the dictionary union operator is nice, it has a very niche use, and I don’t suspect I’ll be using it very often. On the other hand, type hinting is something that I think is really starting to catch on with the popularity of TypeScript. Personally, I’ve been using type hinting for awhile (see image-titler), so any improvements to the process would be welcomed.
Well, apparently, people have been complaining that it’s sort of annoying to write out type hints for generic types like lists. For example, prior to Python 3.9, if you wanted to specify that a function returned a list of strings, you’d have to do something like this (source image-titler):
def process_images(**kwargs) -> List[Image.Image]: """ The main entry point for any image editing. This function will never return an empty list. If no settings are provided, this function will return a default image with a default title. :return: None """ is_batch: bool = kwargs.get(KEY_BATCH) images = list() if is_batch: kwargs[KEY_PATH] = kwargs.get(KEY_PATH) if kwargs.get(KEY_PATH) else TRC_IMAGES images = _process_batch(**kwargs) else: kwargs[KEY_PATH] = kwargs.get(KEY_PATH) if kwargs.get(KEY_PATH) else TRC_IMAGE kwargs[KEY_TITLE] = kwargs.get(KEY_TITLE) if kwargs.get(KEY_TITLE) else _convert_file_name_to_title(**kwargs) images.append(_process_image(**kwargs)) return images
Which unfortunately requires a special import:
from typing import List
Now, having used this typing library for awhile, I wasn’t really bothered by this. That said, I can totally understand why new folks would be annoyed. In fact, the rationale for the feature that simplifies this states:
This change removes the necessity for a parallel type hierarchy in the typing module, making it easier for users to annotate their programs and easier for teachers to teach Python.
Heck, I’m a teacher! So, I’m totally onboard. That said, what exactly did they change? Well, Python 3.9 now lets us type hint standard collections exactly as they appear normally. In other words, instead of using List
, we can use list
directly. Here’s what that would look like:
def process_images(**kwargs) -> list[Image.Image]: """ The main entry point for any image editing. This function will never return an empty list. If no settings are provided, this function will return a default image with a default title. :return: None """ is_batch: bool = kwargs.get(KEY_BATCH) images = list() if is_batch: kwargs[KEY_PATH] = kwargs.get(KEY_PATH) if kwargs.get(KEY_PATH) else TRC_IMAGES images = _process_batch(**kwargs) else: kwargs[KEY_PATH] = kwargs.get(KEY_PATH) if kwargs.get(KEY_PATH) else TRC_IMAGE kwargs[KEY_TITLE] = kwargs.get(KEY_TITLE) if kwargs.get(KEY_TITLE) else _convert_file_name_to_title(**kwargs) images.append(_process_image(**kwargs)) return images
It’s subtle, but this change is amazing. Not only do we not need to import a special library, but we also don’t need to try to remember which casing to use for our collections (e.g. ‘list’ vs. ‘List’).
There’s no doubt I’ll be revisiting projects to take advantage of this feature. Maybe I’ll take a quick pass over all the code snippets in the Python section of the Sample Programs repo. Who knows!
String Methods to Remove Prefixes and Suffixes
Finally, the last feature I want to talk about today is the addition of two new methods to the string class. Apparently, a lot of folks had been using the lstrip()
and rstrip()
methods of string to remove prefixes and suffixes, respectively. In particular, they had assumed that the input was a substring when it was actually a character set which caused a lot of bugs.
As a result, the dev team felt it would be appropriate to provide the behavior that folks were expecting through two new methods:
removeprefix()
removesuffix()
These methods accept a substring and remove it if it exists. Of course, strings are immutable, so these methods return new strings. That said, I suspect it will relieve a ton of headache. I’m really excited to see where this will be used in real world contexts.
Looking Ahead to Python 3.10
With Python 3.9 officially out the door, it’ll only be a year before 3.10 rolls out. If you’re interested in what’s planned for the next version, check out PEP 619. As far as I can tell, the dev team released the first alpha on the same day they released 3.9, so you can already test out the following feature: PEP 618—Add Option Length-Checking to Zip.
Like I said, the Python team does a great job of keeping their language fresh and new. Also, it’s nice to know that the team is looking to help developers out by including features that make out lives easier.
That said, I think it’s helpful to be critical of any new features. For instance, I once raised some concerns about the walrus operator in Python 3.8. I think the last thing we want is a language that’s hard to read and maintain.
Overall, I’m quite pleased with Python 3.9, and I’m excited to see how Python 3.10 shapes up. In the meantime, I’ll keep writing solutions to everyday problems! If you’re looking for somewhere to start, check out these articles:
- 100 Python Code Snippets for Everyday Problems
- How to Merge Two Dictionaries in Python: Comprehensions, Unpacking, and the Union Operator
- The Controversy Behind The Walrus Operator in Python
Likewise, you can help me grow this site by visiting this list where you’ll find links to our community Discord, my YouTube channel, and the newsletter.
In addition, here are some resources from Amazon to help you learn Python (#ad):
- Effective Python: 90 Specific Ways to Write Better Python
- Python Tricks: A Buffet of Awesome Python Features
- Python Programming: An Introduction to Computer Science
Thanks again for check out the site! I appreciate it. Take care.
Recent Code Posts
Unpacking the Jargon Around Compilers, Interpreters, and More
Today, we're going to get a little pedantic and try to define concepts like compiler and interpreter. Of course, I ultimately don't think it matters what the exact definitions are for practical...
Generally, people think of Python as a messy language because it lacks explicit typing and static type checking. But, that's not quite true in modern times. Surely, we can take advantage of type...