Python как компилируемый статически типизированный язык программирования

По данным широко известного в узких кругах Tiobe Index язык Python скорее всего станет языком 2020 года, в четвертый раз в своей карьере. Кроме того, скорее всего он обгонит Java и займет вторую строчку в общем рейтинге языков программирования вслед за языком C.

Основным драйвером роста популярности языка Python стало его широкое использование в задачах машинного обучения. Но как же так? Python динамически типизируемый и интерпретируемый язык. Это же все очень медленно. Как его использовать в научных вычислениях, которые требуют максимальной производительности.

Обычно считается, что Python это только обертка над вычислительным ядром, написанным на C++. А что еще можно ожидать от такого медленного языка? Но ядро то ядром, но местами хочется обработать данные здесь и сейчас на таком удобном Python.

За всю историю Python было придумано большое количество решений, позволяющих ускорить Python код: a) Fortran, C, C++ модули; b) NumPy массивы; c) Cython расширения, и много другого. Это все работало, конечно, но все это было лишь внешним кодом по отношению к Python. Местами довольно неуклюжим.

А что собственно можно еще было сделать? Для быстрого кода нужны типы. А потом, все это как-то надо было компилировать в рамках интерпретируемого языка. Звучит как-то не очень реально. Но! Это все работает, прямо сейчас.

Две дороги: python аннотации типов и LLVM jit компиляция сошлись в релизе Numba 0.52.0. Смотрим на код, который говорит сам за себя.

from typing import List
from numba.experimental import jitclass
from numba.typed import List as NumbaList

@jitclass
class Counter:
    value: int

    def __init__(self):
        self.value = 0

    def get(self) -> int:
        ret = self.value
        self.value += 1
        return ret

@jitclass
class ListLoopIterator:
    counter: Counter
    items: List[float]

    def __init__(self, items: List[float]):
        self.items = items
        self.counter = Counter()

    def get(self) -> float:
        idx = self.counter.get() % len(self.items)
        return self.items[idx]

items = NumbaList([3.14, 2.718, 0.123, -4.])
loop_itr = ListLoopIterator(items)

Каждый класс компилируется с помощью LLVM в нативный код платформы с использованием аннотаций типов.

Добро пожаловать в строго типизированный компилируемый Python!