Почему Python считается медленным языком
Что проверяют
Понимаете ли вы, из чего складывается цена одной операции в CPython, или повторяете школьное «интерпретируемый значит медленный». Заодно смотрят, умеете ли отделять настоящее узкое место от фольклора: большинство сервисов на Python упирается вовсе не в интерпретатор.
Как отвечать
Начните с того, что медленный не язык, а конкретная реализация, обычно CPython. Причины называйте конкретные:
- динамическая типизация: тип известен только в рантайме, поэтому сложение это поиск метода у объекта, а не одна инструкция процессора;
- всё объект: даже небольшое целое число это структура со счётчиком ссылок и указателем на тип, а список это массив указателей, а не чисел подряд, отсюда промахи кэша;
- нет JIT в классическом CPython: байткод крутится в цикле интерпретатора без компиляции в машинный код;
- дорогие вызовы и доступ к атрибутам: атрибут ищется по словарям экземпляра и класса;
- GIL: потоки не считают параллельно на разных ядрах.
Дальше переведите в практику: исходник компилируется в байткод один раз и кэшируется в .pyc, тяжёлые вычисления выносят в библиотеки на C вроде NumPy, в Cython или Numba, а для процессорной параллельности берут multiprocessing. В свежих версиях CPython интерпретатор специализирует часто исполняемый байткод, и появилась сборка с отключаемым GIL.
Где спотыкаются
Говорят «Python выполняет код построчно». Это неверно: модуль разбирается и компилируется в байткод целиком, повторный запуск берёт готовый .pyc. Медленно исполняется байткод, а не текст программы.
Второе: валят всё на GIL. На однопоточный код он не влияет никак, а операции ввода-вывода его отпускают, поэтому веб-сервис, который ходит в базу и в сеть, страдает от него мало.
Третье: предлагают «переписать на C» до профилирования. Обычно больше даёт смена алгоритма или структуры данных, а cProfile показывает, что время уходит в одну функцию, а не размазано по интерпретатору.