Что нового в Python 3.8?

Рассмотрим наиболее интересные дополнения и изменения, которые были внесены в язык Python с релизом 3.8.

Walrus operator

Введение walrus operator – это, наверное, наиболее значимое изменение, которое принес с собой Python 3.8. Этот оператор (выглядит он так (:=)) позволяет присваивать значение переменной в выражении. Таким образом код становится ещё более лаконичным.

Рассмотрим несколько примеров.

Присвоение внутри арифметического выражения:

>>> # Python <= 3.7
>>> a = 5
>>> b = 7
>>> c = a + b
>>> print('a = {}, b = {}, c = {}'.format(a, b, c))
a = 5, b = 7, c = 12

>>> # Python 3.8
>>> c = (a := 5) + (b := 7)
>>> print('a = {}, b = {}, c = {}'.format(a, b, c))
a = 5, b = 7, c = 12

Присвоение внутри условного оператора:

>>> a = [1, 2, 3, 4, 5]
>>> if ((n := len(a)) >= 3):
        print(f"len = {n} high then limit")

len = 5 high then limit

Присвоение внутри оператора цикла while:

>>> while ((msg := input()) != 'q'):
        print(f"> {msg}")

test
> test

hello
> hello

q

Обратите внимание, выражение с оператором walrus должно быть помещено в скобки. Более подробное см. PEP 572

Positional-only arguments

Еще одни нововведением в синтаксисе является возможность определить для ряда аргументов функции только позиционную передачу значений. Как известно, в Python, можно использовать имена аргументов при передачи значений в функцию:

Создадим функцию:

>>> def print_pair(x, y):
        print(f"({x}, {y})")

Для этой функции возможны следующие варианты вызова:

>>> print_pair(1, 2)
(1, 2)

>>> print_pair(1, y=2)
(1, 2)

>>> print_pair(x=1, y=2)
(1, 2)

>>> print_pair(y=2, x=1)
(1, 2)

Если необходимо запретить возможность именовать аргументы при вызове переменной, то для этого в Python 3.8 можно использовать специальный символ: ‘/’:

>>> def print_pair(x, y, /):
        print(f"({x}, {y})")

В этом случае будет возможен только один вариант вызова функции:

>>> print_pair(1, 2)
(1, 2)

Другие (с именованием аргументов) будут недоступны:

>>> print_pair(1, y=2)
Traceback (most recent call last):
  File "<pyshell#44>", line 1, in <module>
    print_pair(1, y=2)
TypeError: print_pair() got some positional-only arguments passed as keyword arguments: 'y'

>>> print_pair(x=1, y=2)
Traceback (most recent call last):
  File "<pyshell#45>", line 1, in <module>
    print_pair(x=1, y=2)
TypeError: print_pair() got some positional-only arguments passed as keyword arguments: 'x, y'

Более подробное см. PEP 570

f-строки: поддержка метки “=”

Следующее новшество касается f-строк: добавлена метка “=”, которая используется следующим образом  f'{expr=}’. В результате будет вычислено выражение и этот результат будет присвоен текстовому представлению выражения. Примеры:

>>> name = "John"
>>> age = 10

>>> # Python <= 3.7
>>> print(f"name='{name}', age={age}")
name='John', age=10

>>> # Python 3.8
>>> print(f"{name=}, {age=}")
name='John', age=10

Пример с вычислением:

>>> print(f"{4+5=}")
4+5=9

Новые типы: TypedDict, Literal, Final, Protocol

TypedDict

Для аннотации переменных типа dict с известным набором и типом элементов можно использовать TypedDict, для этого создайте класс-наследник от TypedDict:

>>> from typing import TypedDict

>>> class Persone(TypedDict):
        name: str
        age: int

После этого имя класса можно использовать как тип в аннотации:

>>> user: Persone = {'name': 'Alex', 'age': 32}
>>> user
{'name': 'Alex', 'age': 32}

Literal

Literal используется, если нужно указать, что переменная должна принять значение из заданного набора:

def get_status(port: int) -> Literal['connected', 'disconnected']:
    ...

Final

Final используется для указания на то, что переменная (или атрибут) не подлежит изменению или переопределению:

from typing import Final

# Пример с переменной
PI: Final = 3.14
PI = 4 # Будет вызвана ошибка при проверке типов

# Пример с атрибутом класса
class Port:
    TIMEOUT: Final[int] = 1000

class SerialPort(Port):
    TIMEOUT = 500  # Будет вызвана ошибка при проверке типов

Protocol

Базовый класс для классов протоколов. Классы протокола определяются следующим образом:

class Engine(Protocol):
    def start(self) -> bool:
        ...

Пример использования протокола:

class SteamEngine:
    def start(self) -> bool:
        return True

def test(x: Engine) -> bool:
    return x.start()

test(SteamEngine())

Дополнительная информация

Дополнительно по новшествам Python 3.8 можете прочитать здесь:

https://realpython.com/python38-new-features/

https://docs.python.org/3/whatsnew/3.8.html

https://deepsource.io/blog/python-3-8-whats-new/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *