The latest, greatest version of Python is going to be out in beta soon. While there’s still some time before the final stable version is available, it is worth looking into all that’s new. Python 3.8 adds some new syntax to the language, a few minor changes to existing behavior, and mostly a bunch of speed improvements — maintaining the tradition from the earlier 3.7 release.

This post outlines the most significant additions and changes you should know about Python 3.8. Take a look!

1. The walrus operator

Assignment expressions have come to Python with the “walrus” operator := . This will enable you to assign values to a variable as part of an expression. The major benefit of this is it saves you some lines of code when you want to use, say, the value of an expression in a subsequent condition.

So, something like this:

line = f . readline () while line : ... # process line line = f . readline ()

Can now be written in short like this:

while line : = f . readline (): ... # process line

Yay for brevity, but some might say this affects readability of code — it can be argued that the first variant here is clearer and explicit. This discussion was the center of a major controversy in the Python community.

2. Positional-only arguments

A special marker, / , can now be used when defining a method’s arguments to specify that the functional only accepts positional arguments on the left of the marker. Keyword-only arguments have been available in Python with the * marker in functions, and addition of / marker for positional-only arguments improves the language’s consistency and allows for a robust API design.

Take an example of this function:

def pow ( x , y , z = None , / ): r = x ** y if z is not None : r %= z return r

The / marker here means that passing values for x , y and z can only be done positionally, and not using keyword arguments. The behavior is illustrated below:

>>> pow ( 2 , 10 ) # valid >>> pow ( 2 , 10 , 17 ) # valid >>> pow ( x = 2 , y = 10 ) # invalid, will raise a TypeError >>> pow ( 2 , 10 , z = 17 ) # invalid, will raise a TypeError

A more detailed explanation on the motivation and use-cases can be found in PEP 570.

3. f-strings now support “=”

Python programmers often use “printf-style” debugging. In the old days this was pretty verbose:

print "foo=" , foo , "bar=" , bar

f-strings make this a bit nicer:

print ( f "foo={foo} bar={bar}" )

But you still have to repeat yourself: you have to write out the string “foo”, and then the expession “foo”.

The = specifier, used as f'{expr=}' expands to the text of the expression, an equal sign, then the repr of the evaluated expression. So now, you can simply write:

print ( f "{foo=} {bar=}" )

A small step for the language, but a giant leap for everyone who sprinkles print() statements for debugging!

4. reversed() now works with dict

Since Python 3.7, dictionaries preserve the order of insertion of keys. The reversed() built-in can now be used to access the dictionary in the reverse order of insertion — just like OrderedDict .

>>> my_dict = dict ( a = 1 , b = 2 ) >>> list ( reversed ( my_dict )) [ 'b' , 'a' ] >>> list ( reversed ( my_dict . items ())) [( 'b' , 2 ), ( 'a' , 1 )]

5. Simplified iterable unpacking for return and yield

This unintentional behavior has existed since Python 3.2 which disallowed unpacking iterables without parentheses in return and yield statements.

So, the following was allowed:

def foo (): rest = ( 4 , 5 , 6 ) t = 1 , 2 , 3 , * rest return t

But these resulted in a SyntaxError :

def baz (): rest = ( 4 , 5 , 6 ) return 1 , 2 , 3 , * rest

def baz (): rest = ( 4 , 5 , 6 ) yield 1 , 2 , 3 , * rest

6. New syntax warnings

The latest release fixes this behavior, so doing the above two approaches are now allowed.

The Python interpreter now throws a SyntaxWarning in some cases when a comma is missed before tuple or list. So when you accidentally do this:

data = [ ( 1 , 2 , 3 ) # oops, missing comma! ( 4 , 5 , 6 ) ]

Instead of showing TypeError: 'tuple' object is not callable which doesn’t really tell you what’s wrong, a helpful warning will be shown pointing out that you probably missed a comma. Pretty helpful while debugging!

The compiler now also produces a SyntaxWarning when identity checks ( is and is not ) are used with certain types of literals (e.g. strings, integers, etc.). You rarely want to compare identities with literals other than None , and a compiler warning can help avoid a number of elusive bugs.

7. Performance improvements

This release adds a number of performance speed-ups to the interpreter, following suit from the previous 3.7 release.

operator.itemgetter() is now 33% faster. This was made possible by optimizing argument handling and adding a fast path for the common case of a single non-negative integer index into a tuple (which is the typical use case in the standard library).

Field lookups in collections.namedtuple() are now more than two times faster, making them the fastest form of instance variable lookup in Python.

The list constructor does not over-allocate the internal item buffer if the input iterable has a known length (the input implements len). This makes the created list 12% smaller on average.

Class variable writes are now twice as fast: when a non-dunder attribute was updated, there was an unnecessary call to update slots, which is optimized.

Invocation of some simple built-ins and methods are now 20-50% faster. The overhead of converting arguments to these methods is reduced.

uuid.UUID now uses slots to reduce it’s memory footprint.

Summary

The upcoming release of Python adds some great new features to the language and significantly improves the performance with fundamental speed-up fixes. There is a small number behavior changes that might require modifying existing code while upgrading to Python 3.8, but the performance gains and new syntax make it totally worth the effort. A detailed change log of all that’s new can be found here.

Japanese version of this post, thanks to Hiroya Akita.