Python 3.15 релизнулся 9 октября. Khuyen Tran из CodeCut выбрала пять изменений, которые больше всего пригодятся в работе с данными, и для каждого показала старый и новый способ с реальными цифрами. Собрали.
1. Ленивые импорты (PEP 810)
Тяжёлый пакет можно пометить словом lazy, и тогда он подгрузится только при первом обращении, а не на старте скрипта.
# было: pandas грузится сразу, даже если не используется
import pandas as pd
print("hello")
# стало: ничего не грузится, пока pd реально не понадобился
lazy import pandas as pd
print("hello")
df = pd.read_parquet("taxi.parquet") # вот тут pandas и загрузится
Замер скрипта, который импортирует pandas, но не использует его (медиана из 20 запусков):
обычный импорт: 280 ms
lazy: 14 ms (в 20 раз быстрее старта)
2. Встроенный frozendict (PEP 814)
Неизменяемый словарь. Удобно для общего конфига: функция уже не сможет его втихую перезаписать.
# было: обычный dict любая функция может поменять
config = {"min_fare": 0, "max_fare": 500}
def drop_outliers(cfg):
cfg["max_fare"] = 1000 # задумано как локальная правка
drop_outliers(config)
# config теперь {'min_fare': 0, 'max_fare': 1000}, подменился молча
# стало:
config = frozendict(min_fare=0, max_fare=500)
config["max_fare"] = 1000
# TypeError: 'frozendict' object does not support item assignment
Изменённую копию делаем через |:
strict = config | {"max_fare": 300}
# original: frozendict({'min_fare': 0, 'max_fare': 500})
# new: frozendict({'min_fare': 0, 'max_fare': 300})
3. Распаковка внутри comprehension (PEP 798)
Звёздочку и двойную звёздочку теперь можно использовать прямо в выражениях.
column_groups = [["fare", "tip"], ["distance"], ["passengers", "payment"]]
# было: двойной for
[column for group in column_groups for column in group]
# стало:
[*group for group in column_groups]
# ['fare', 'tip', 'distance', 'passengers', 'payment']
# слияние словарей, последний перекрывает ключи
layers = [{"min_fare": 0, "max_fare": 500}, {"max_fare": 300}]
{**layer for layer in layers}
# {'min_fare': 0, 'max_fare': 300}
4. Профайлер Tachyon
Сэмплирующий профайлер смотрит, какая функция выполняется, примерно раз в миллисекунду, и умеет подключаться к уже запущенному процессу, чего cProfile не умеет.
# запуск с сортировкой и топ-9 строк
python -m profiling.sampling run --sort cumtime -l 9 pipeline.py
# подключение к работающей задаче на 5 секунд
python -m profiling.sampling attach -d 5 <PID>
На пайплайне с данными такси накладные расходы минимальны:
python pipeline.py ~4.9 s без профайлера
profiling.sampling run pipeline.py ~5.1 s Tachyon: +4%
python -m cProfile pipeline.py ~16.7 s cProfile: в 3.4 раза медленнее
5. Понятные сообщения об ошибках
При опечатке в имени атрибута или метода Python теперь подсказывает, что ты, вероятно, имел в виду.
exp.shape
# было: AttributeError: 'Experiment' object has no attribute 'shape'
# стало: ...has no attribute 'shape'. Did you mean '.data.shape' instead of '.shape'?
["fare", "tip"].push("distance")
# было: 'list' object has no attribute 'push'
# стало: 'list' object has no attribute 'push'. Did you mean '.append'?
