Python 3.9 is scheduled for 5th October 2020. There is still a long way to go until then, however, with the latest alpha 3.9.0a6 released last week and the beta version is just around the corner, we can already discuss new features. In this article, we explore some features that we have found interesting and that we think will make our code cleaner. A usual with each Python release, there are several big topics and a couple of smaller features. It is important to note that previous minor versions of Python 3 still contained some functions for backward compatibility with Python 2.7. Since support for Python 2.7 officially stopped a lot of functions will be removed or marked as deprecated. Keep this in mind if you want to make a jump from 2.7 to 3.9. We don’t focus on these removed functionalities in this article, but rather on differences from Python 3.8. So, with that cleared up, let’s dive in.

We don’t do sales, but given the circumstances and the severity of the situation, we decided to change that. Don’t be fooled, this sale isn’t meant for profit and it’s most definitely not planned.  This sale is here to help people who want to become better, learn new skills and be more productive than ever before. Our book offers are on a 50% sale.

Installing Alpha/Beta Version

Before we start, let’s first see how we can install alpha and beta versions in our machines, without messing up the current distribution that we use.

Linux

Linux To install the latest alpha on the Linux machine, first, get the installation zip using wget:

wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0a6.tgz

Unpack it:

tar xzvf Python-3.9.0a6.tgz
cd Python-3.9.0a6
./configure --prefix=$HOME/python-3.9.0a56

And run the make file:

make
make install
$HOME/python-3.9.0a6/bin/python3.9

Windows and Mac

To install the latest alpha on Windows or Mac machine, first, get the installation file from this link and run the installation file. Then it is just a matter of going through the setup:

Merge and Update Dictionary Operators

Now, we are ready for takeoff, and the first new feature we explore is a new way to update and merge dictionaries. As a reminder, dictionaries are collections that are indexed and changeable, but unordered as well. These data structures are composed of key-value pairs. In Python, they are defined like this:

blog_dict1 = {
    'name': 'Rubik\'s Code',
    'address': 'www.rubikscode.net'
}

print(blog_dict1['name'])
print(blog_dict1.get('address'))
Rubik's Code
www.rubikscode.net

Until now, merging two dictionaries in Python was, well, kinda weird. Let’s add another dictionary that also contains information for same entity:

blog_dict2 = {
    'num_articles': 211,
    'num_authors': 3
}
Merge Dictionaries

So, if we wanted to merge these two dictionaries before version 3.9, we could do that in a couple of ways. We could use copy/update combo:

blog_dict_merged = blog_dict1.copy()
blog_dict_merged.update(blog_dict2)

We created a copy of the blog_dict1 because the disadvantage of the update method is that it modifies the dictionary in-place. Another way for merging dictionaries would be something like this:

blog_dict_merged = {**blog_dict1, **blog_dict2}

The ** operator unpacks elements from the dictionary. It is there since Python 3.5+. As you can see this is not intuitive and it takes a while to get used to it. In Python 3.9 you can use union operator | for merging two dictionaries, like this:

blog_dict_merged = blog_dict1 | blog_dict2

Clean, cool and intuitive.

Builtin Generic Types

If you worked with languages Java or C# you are aware with the concept of generic types. These are ussually collections that can be parametrized with the type that they contain. For example, in C# you can define a list like this:

var exampleList = new List<string>();

Which means that you are creating a list of strings. Now Python is introducing this kind of functionality into the typing module. Basically, tooling along with type checkers and linters are modified to recognize standard collections as generics. This means that you don’t need to import corresponding capitalized types (e.g. List) from typing. In a nutshell, this feature removes the necessity for a parallel type hierarchy in the typing module and eases up the annotations of the program. Here is what that looks like:

def console_printer(items: list[str]) -> None:
    for item in items:
        print(item)

You can also instantiate objects of generic collections:

generic_list = list[str]()

PEG Parser

This feature is a bit low level and it doesn’t affect the developers directly as much. This might be interesting to the engineers that are interested in compilers. Thus far Python was using LL(1)-based parser and all Python grammar was kind of built around this parser. LL(1) parser is a top-down parser that parses the input from left to right. First, it performs leftmost derivation of the sentence, with just one token of lookahead. As a result, it creates a table that contains encoded transitions between all possible states of the parser – the parse table.

LL(1) Parser

Because of the nature of Python and limitations of LL(1) some hacks were introduced into the language. That is why new parser – PEG (Parsing Expression Grammar) is used in Python 3.9. Difference between PEG and LL(1) grammar is that PEG grammar reflects how the parser will operate when parsing it. One example that can be used for understanding this change is the choice operator. Consider this expression:

A | B | C

LL(1) creates a table during the parsing process that decides which alternative (A, B or C) should be expanded. On the other hand, PEG parser first checks if the first condition succeeds and proceeds to other conditions only if it fails. This means that choice operator will no longer be commutative.

Topological Ordering

If you are working with graphs, you’ll find this feature very interesting and useful. New class TopologicalSorter is added to the functools module. This class performs the topological ordering of the graph. How does it work? if we consider an edge that goes from vertex a to vertex b (a->b), in topological ordering vertex a comes before vertex b. Here is how it is used:

graph = {"D": {"B", "C"}, "C": {"A"}, "B": {"A"}}
ts = TopologicalSorter(graph)
tuple(ts.static_order())
('A', 'C', 'B', 'D')

If you wanted to do something like this with previous versions of Python, you would have to implement it on your own using algorithms like a depth-first search or Khan’s algorithm

Graph

Another cool thing about this feature is that it supports the parallel sorting of nodes even if they are added one by one. In this case, you would create TopologicalSorter with an optional initial graph and then add additional nodes to the graph. You would use methods prepare, is_active and get_ready to process each node and then use done method to indicate that node is added and that graph is sorted:

topological_sorter = TopologicalSorter(initial_graph)

# Add nodes

topological_sorter.prepare()
while topological_sorter.is_active():
    for node in topological_sorter.get_ready():
        task_queue.put(node)

    node = finalized_tasks_queue.get()
    topological_sorter.done(node)

New string and math methods

Modules such as string and math are extended with some useful functions too in Python 3.9. The string module has two new methods: removeprefix and removesuffix. With these, you can easily remove an unneeded prefix or a suffix from a string. Modules bytes, bytearray, and collections.UserString are extended with the same methods. Here is to use them:

print("rubikscode".removeprefix("rubiks"))
print("rubikscode".removesuffix("code"))
code
rubiks

Methods like math.gdc and math.ldc, for calculating Greatest Common Divisor and Least Common Multiple respectively, are now extended so they receive more than two parameters. You can use them like this:

import math

print(math.gcd(64,80,264))
print(math.lcm(4, 8, 5))
8
40

Apart from that, new methods are added to the math module:

  • nextafter(a, b) – returns next float number after a going towards b.
  • ulp(a) – return the value of the least significant bit of a float. ULP stands for “Unit in the Last Place” and it’s used as a measure of accuracy in numeric calculations. For more information on the ULP check this article.

The math.nextafter method can be used like this:

print(math.nextafter(5, 6))
print(math.nextafter(9, 2))
5.000000000000001
8.999999999999998

The math.ulp method can be used like this:

math.ulp(1000000000000000)
0.125

Conclusion

In this article, we had a chance to see some of the features from the upcoming Python 3.9 that we are excited about. Some of these features result in a cleaner and more understandable code, while others might reframe our mindset about Python grammar. If you want to explore these features in more detail and see what else is coming with the new version, check out the official documentation.

Thank you for reading!

Nikola M. Zivkovic

Nikola M. Zivkovic

CAIO at Rubik's Code

Nikola M. Zivkovic a CAIO at Rubik’s Code and the author of book “Deep Learning for Programmers“. He is loves knowledge sharing, and he is experienced speaker. You can find him speaking at meetups, conferences and as a guest lecturer at the University of Novi Sad.

Rubik’s Code is a boutique data science and software service company with more than 10 years of experience in Machine Learning, Artificial Intelligence & Software development. Check out the services we provide.

Discover more from Rubix Code

Subscribe now to keep reading and get access to the full archive.

Continue reading