Обзор Python 3.8

Релиз Python 3.8 намечен на октябрь 2019 года, но уже сейчас у каждого есть возможность пощупать набор новых фишек языка. Пока пишу этот пост, на официальном сайте доступна версия python 3.8b2.

Итак, что же нам готовит релиз грядущий?

f-string =

Теперь выводить debug информацию стало ещё проще и красивее. Достаточно в f-string передать знак =:

>> a = 123
>> b = 456
>> print(f'{a=} and {b=}')
a=123 and b=456

Assignment Expressions или :=, он же walrus operator

Легендарный PEP572 из-за которого Гвидо сложил полномочия диктатора Python. Оператор выполняет присваивание значения переменной в выражении. Например, вам хочется проверить есть ли ключ в словаре, а также присвоить значение этого ключа переменной. В версиях Python до 3.8 необходимо сделать:

params = {'foo': 'bar'}
x = params.get('foo')

if x:
    print(x)

В версии 3.8 код выглядит чуть компактнее:

params = {'foo': 'bar'}
if x := params.get('foo'):
    print(x)

Positional-only arguments

В python 3 в далёком 2006 году появились keyword-only arguments (PEP3102). Их задача заключается в навязывании передачи аргументов функции или методу по ключу после символа *

def test(a, b, *, key1, key2):
    pass

test(1,2,3,4)
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    test(1,2,3,4)
TypeError: test() takes 2 positional arguments but 4 were given</module></pyshell#7>

Интерпретатор сообщает нам, что функция принимает только 2 позиционных аргумента, но передано было 4. Чтобы он не ругался, функцию необходимо вызывать так:

test(1, 2, key1=3, key2=4)

В python 3.8 появилась возможность задавать передачу аргументов исключительно согласно их позиции. В отличие от keyword-only arguments, все аргументы обязаны быть переданы согласно позиции:

def position_only(a, b, c, d=0, /):
    pass

position_only(1, 2, 3)  # OK

position_only(1, 2, c=3)  # вызов такой функции провоцирует исключение
Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    position_only(1,2,c=3)
TypeError: position_only() got some positional-only arguments passed as keyword arguments: 'c'</module></pyshell#11>

Общая память для IPC

Для межпроцессорного взаимодействия в python 3.8 появилась общая память. Раньше, чтобы передавать данные между процессами внутренний механизм подразумевал сериализацию и десериализацию объекта и отправку данных в сокет. С использованием механизма ОС по выделению общей памяти, отпадает необходимость взаимодействия через диск или сокет, а это даёт значительный рост к производительности. В пакете multiprocessing появился модуль shared_memory. Пример использования общей памяти между двумя разными процессами:

from multiprocessing import shared_memory
a = shared_memory.ShareableList(range(5))
print(a.shm.name)
>>> 'wnsm_bd6b5302'

Открываем другой shell и считываем данные из общей памяти:

from multiprocessing import shared_memory
b = shared_memory.ShareableList(name='wnsm_bd6b5302')
print(b)
>>> ShareableList([0, 1, 2, 3, 4], name='wnsm_bd6b5302')

TypedDict

Появилась возможность указывать тип значений у словарей с фиксированным количеством строковых ключей. Например:

from typing import TypedDict

class Movie(TypedDict):
    title: str
    year: int

movie: Movie = {
    'title': 'Catch me if you can', 
    'year': 2002
}

Класс Movie можно использовать в аннотациях:

from typing import List
def my_function(films: List[Movie]):
    pass

final

Те, кто знаком с Java сразу понимают что такое final. То, что помечено final не должно изменяться во время жизненного цикла скрипта.

from typing import Final, final

birth_year: Final = 1989
birth_year = 1901  # вот тут IDE или type checker вроде mypy должен ругаться

Классы, помеченные декоратором final, не должны быть унаследованы:

@final
class FinalClass:
    pass

class InheritedFromFinal(FinalClass):  # нельзя наследовать класс FinalClass
    pass

Также можно формально запретить переопределение метода в дочернем классе:

class MyClass:
    @final
    def prohibited(self):
        pass

class SecondClass(MyClass):
    def prohibited(self):  # вот тут type checker должен дико материться
        pass

Это далеко не все фишечки в новой версии Python. Более подробно о новинках можно узнать по ссылке.