What's new in Python 3.8?
Tags: python, discuss
Published: 2019-07-26 08:05:04

The latest, greatest version of Python is going to be out in beta soon. While there's still [some time](https://www.python.org/dev/peps/pep-0569/#release-schedule) 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 &mdash; 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:
```python
length = len(my_list)
if length > 10:
    print(f"List is too long ({length} elements, expected <= 10)")
```

Can now be written in short like this:
```python
if (length := len(my_list)) > 10:
    print(f"List is too long ({length} elements, expected <= 10)")
```

Yay for brevity, but some might say this affects readability of code &mdash; it can be argued that the first variant here is clearer and explicit. This discussion was the center of a [major controversy](https://lwn.net/Articles/757713/) 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:
```python
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:

```python
>>> 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](https://www.python.org/dev/peps/pep-0570/).


## 3. f-strings now support "="

Python programmers often use "printf-style" debugging. In the old days this was pretty verbose:
```python
print "foo=", foo, "bar=", bar
```

f-strings make this a bit nicer:
```python
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:

```python
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 &mdash; just like `OrderedDict`.

```python
>>> 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:
```python
def foo():
    rest = (4, 5, 6)
    t = 1, 2, 3, *rest
    return t
```

But these resulted in a `SyntaxError`:
```python
def baz():
    rest = (4, 5, 6)
    return 1, 2, 3, *rest
```

```python
def baz():
    rest = (4, 5, 6)
    yield 1, 2, 3, *rest
```
The latest release fixes this behavior, so doing the above two approaches are now allowed.


## 6. New syntax warnings

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:
```python
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](https://docs.python.org/3.8/whatsnew/3.8.html#porting-to-python-3-8) 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](https://docs.python.org/3.8/whatsnew/3.8.html).

_Originally posted on [DeepSource Blog](https://deepsource.io/blog/python-3-8-whats-new/)_.