Профайлинг и Оптимизация Производительности
Когда ваш код уже не просто работает, а должен летать, на первый план выходит умение профилировать и оптимизировать производительность. Для Python это не просто трюки, а глубокое понимание CPython и его инструментов.
Шаг 1: Точное Измерение (Profiling)
Прежде чем оптимизировать, мы должны знать, где именно тратятся ресурсы. Слепое улучшение кода — это путь к бессмысленным изменениям и появлению новых багов.
- Модуль
- Line-by-line Profilers (
- Анализ Памяти (
Шаг 2: Реальная Оптимизация (Beyond Simple Fixes)
Найдя узкое место, приступаем к устранению, используя знания об устройстве Python.
1. Уменьшение Накладных Расходов Интерпретатора (Interpreter Overhead)
- Использование Встроенных Функций и Модулей C: Встроенные функции (например,
- Пример: Используйте
2. Манипуляции с Данными NumPy/Pandas
- Векторизация: Если вы работаете с числовыми данными, полностью переходите на NumPy и Pandas. Вместо циклов
3. Параллелизм vs. Конкурентность
- CPU-bound задачи (расчеты): Из-за GIL (Global Interpreter Lock) чистый Python не может эффективно использовать несколько ядер ЦП для одновременного выполнения кода на Python. Используйте модуль
- I/O-bound задачи (сеть, диск): Если код проводит много времени в ожидании (ввод-вывод), используйте конкурентность с помощью
📲 Мы в MAX
👉@BookPython
Когда ваш код уже не просто работает, а должен летать, на первый план выходит умение профилировать и оптимизировать производительность. Для Python это не просто трюки, а глубокое понимание CPython и его инструментов.
Шаг 1: Точное Измерение (Profiling)
Прежде чем оптимизировать, мы должны знать, где именно тратятся ресурсы. Слепое улучшение кода — это путь к бессмысленным изменениям и появлению новых багов.
- Модуль
cProfile: Стандартный и самый надежный инструмент для нахождения горячих точек (hotspots) — функций, где тратится больше всего времени. Он показывает совокупное время (tottime) и общее время (cumtime) выполнения каждой функции, включая вызовы извне.
import cProfile
import re
cProfile.run('re.compile("foo|bar")', filename='profile_data')
# Анализируем результаты
import pstats
p = pstats.Stats('profile_data')
p.sort_stats('cumulative').print_stats(10)
- Line-by-line Profilers (
line_profiler): Если cProfile показывает функцию, которая "тормозит", но вам нужно понять, какая именно строка внутри этой функции виновата, используйте внешние инструменты, такие как line_profiler.
# Декоратор @profile над нужной функцией
# Запуск: kernprof -l my_noscript.py
# Анализ: python -m line_profiler my_noscript.py.lprof
- Анализ Памяти (
memory_profiler): Для задач, связанных с большими данными или длительными процессами, где утечки памяти или излишнее потребление критичны, используйте memory_profiler или pympler.Шаг 2: Реальная Оптимизация (Beyond Simple Fixes)
Найдя узкое место, приступаем к устранению, используя знания об устройстве Python.
1. Уменьшение Накладных Расходов Интерпретатора (Interpreter Overhead)
- Использование Встроенных Функций и Модулей C: Встроенные функции (например,
sum, map, sorted) и функции из стандартной библиотеки, написанные на C (например, itertools, collections), работают значительно быстрее, чем их эквиваленты на чистом Python, поскольку избегают накладных расходов на GIL и байткод.- Пример: Используйте
collections.deque вместо list для быстрого добавления/удаления с обоих концов.2. Манипуляции с Данными NumPy/Pandas
- Векторизация: Если вы работаете с числовыми данными, полностью переходите на NumPy и Pandas. Вместо циклов
for в Python, обрабатывающих элементы по одному, используйте векторизованные операции. Это позволяет выполнять вычисления на уровне C/Fortran, эффективно используя процессор.3. Параллелизм vs. Конкурентность
- CPU-bound задачи (расчеты): Из-за GIL (Global Interpreter Lock) чистый Python не может эффективно использовать несколько ядер ЦП для одновременного выполнения кода на Python. Используйте модуль
multiprocessing для распараллеливания задачи между процессами.- I/O-bound задачи (сеть, диск): Если код проводит много времени в ожидании (ввод-вывод), используйте конкурентность с помощью
asyncio (асинхронный ввод-вывод) или threading. В этом случае GIL не мешает, так как Python "освобождает" его во время ожидания.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
В Python множества поддерживают операторы сравнения, где
Это означает, что множества частично упорядочены, то есть существуют такие
Некоторые функции, такие как
📲 Мы в MAX
👉@BookPython
a < b означает, что a является подмножеством b:
>>> {1} < {1, 2}
True
>>> {1} < {2, 3}
False
Это означает, что множества частично упорядочены, то есть существуют такие
a и b, что и a < b, и b < a — ложны:
>>> {1} < {2, 3}
False
>>> {1} > {2, 3}
False
Некоторые функции, такие как
min, max и sorted, требуют полного порядка, поэтому их применение к списку множеств может дать неожиданные результаты:
>>> min([{1}, {2}])
{1}
>>> min([{2}, {1}])
{2}
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Иногда вам нужно очистить коллекцию в Python. Вы, вероятно, используете что-то вроде
Правильный способ очистки словаря, множества,
📲 Мы в MAX
👉@BookPython
d = {} (для словарей), но на самом деле это не очистка, а создание новой коллекции и выбрасывание старой. Это может сработать для вас, но другие владельцы того же объекта всё ещё будут иметь ссылку на оригинальный.Правильный способ очистки словаря, множества,
deque и других коллекций — вызвать x.clear(). 👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Иногда вам может понадобиться проверить синтаксис Python-файла без его запуска. Такая простая проверка может быть полезна, например, в качестве хука перед коммитом или быстрой проверки в рамках непрерывной интеграции (CI).
Прямого способа сделать это нет. Вы можете запустить файл с помощью команды
Тем не менее, стандартная библиотека Python содержит модуль
📲 Мы в MAX
👉@BookPython
Прямого способа сделать это нет. Вы можете запустить файл с помощью команды
python -m module.py, что предотвратит выполнение блока if __name__ == '__main__'. Однако все импорты всё равно будут выполнены, и это может привести к ошибкам, если вы хотите проверить синтаксис в среде, где модуль не может и не должен быть запущен.Тем не менее, стандартная библиотека Python содержит модуль
py_compile, который генерирует байт-код из исходного файла Python без его выполнения. Это именно то, что нам нужно:
$ python -m py_compile test.c
File "test.c", line 1
int main() {
^
SyntaxError: invalid syntax
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
CPython поддерживает два уровня оптимизации. Вы можете включить их с помощью флагов
- Флаг
- Флаг
Обычная версия скрипта кэшируется в файл
📲 Мы в MAX
👉@BookPython
-O и -OO.- Флаг
-O устанавливает __debug__ в значение False и удаляет все операторы assert из программы. - Флаг
-OO делает то же самое, а также удаляет строки документации (docstrings).Обычная версия скрипта кэшируется в файл
.pyc, а оптимизированная версия раньше кэшировалась в файл .pyo. Однако, начиная с Python 3.5, файлы .pyo больше не используются. Вместо них, в соответствии с PEP 488, вводятся файлы .opt-1.pyc и .opt-2.pyc.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
«Reduce» — это функция высшего порядка, которая рекурсивно обрабатывает итерируемый объект, применяя некоторую операцию к следующему элементу и уже вычисленному значению. Также вы можете знать её под названиями «fold», «inject», «accumulate» или другими.
Использование
В Python функция
Также, если вам нужны простые лямбда-функции, такие как
📲 Мы в MAX
👉@BookPython
Использование
reduce с result = result + element даёт сумму всех элементов, result = min(result, element) возвращает минимум, а result = element позволяет получить последний элемент последовательности.В Python функция
reduce доступна (начиная с Python 3, она была перемещена в functools.reduce):
from functools import reduce
print(reduce(lambda s, i: s + i, range(10))) # 45
print(reduce(lambda s, i: min(s, i), range(10))) # 0
print(reduce(lambda s, i: i, range(10))) # 9
Также, если вам нужны простые лямбда-функции, такие как
lambda a, b: a + b, в Python есть модуль operator, который упрощает их использование:
from operator import add
print(reduce(add, range(10))) # 45
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
Создание новой переменной в Python фактически означает создание нового имени для уже существующего объекта. Именно поэтому этот процесс называется связыванием имени (name binding).
Существует множество способов связать имя с объектом. Вот примеры того, как можно связать
Также можно привязать имя к объекту, манипулируя глобальным пространством имен:
Однако нельзя сделать то же самое с
📲 Мы в MAX
👉@BookPython
Существует множество способов связать имя с объектом. Вот примеры того, как можно связать
x:
x = y # Присваивание
import x # Импорт модуля
class x: pass # Определение класса
def x(): pass # Определение функции
def y(x): pass # Определение аргумента функции
for x in y: pass # Перебор элементов в цикле
with y as x: pass # Использование в конструкции with
except y as x: pass # Обработка исключения
Также можно привязать имя к объекту, манипулируя глобальным пространством имен:
In : x
NameError: name 'x' is not defined # Ошибка: переменная x не определена
In : globals()['x'] = 42 # Присваивание через globals()
In : x
Out: 42 # Теперь x привязан к 42
Однако нельзя сделать то же самое с
locals(), так как изменения словаря locals() игнорируются.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
Когда в Python используется переменная, сначала она ищется в текущей области видимости. Если такая переменная не найдена, поиск продолжается во вложенной области. Это повторяется до тех пор, пока не будет достигнуто глобальное пространство имен.
Однако присваивание переменной работает иначе. Новая переменная всегда создается в текущей области видимости, если не указано
📲 Мы в MAX
👉@BookPython
x = 1
def scope():
x = 2
def inner_scope():
print(x) # выводит 2
inner_scope()
scope()
Однако присваивание переменной работает иначе. Новая переменная всегда создается в текущей области видимости, если не указано
global или nonlocal:
x = 1
def scope():
x = 2
def inner_scope():
x = 3
print(x) # выводит 3
inner_scope()
print(x) # выводит 2
scope()
print(x) # выводит 1
global позволяет использовать переменные из глобального пространства имен, а nonlocal ищет переменную в ближайшей окружающей области видимости. Сравните:
x = 1
def scope():
x = 2
def inner_scope():
global x
x = 3
print(x) # выводит 3
inner_scope()
print(x) # выводит 2
scope()
print(x) # выводит 3
x = 1
def scope():
x = 2
def inner_scope():
nonlocal x
x = 3
print(x) # выводит 3
inner_scope()
print(x) # выводит 3
scope()
print(x) # выводит 1
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
list позволяет хранить массив из любых объектов. Это довольно удобно, но может быть неэффективно. Для компактного представления массивов базовых значений можно использовать модуль array. Поддерживаемые значения включают различные типы C, такие как char, int, long, double и другие. Фактическое представление определяется реализацией C.
import array
a = array.array('B') # Создаем массив байтов
a.append(240)
a.append(159)
a.append(144)
a.append(180)
print(a.tobytes().decode('utf8')) # Выводит: '🐴'
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Если вам нужно итерироваться по нескольким итерируемым объектам одновременно, функция
Обратите внимание, что
Для распаковки значений можно использовать оператор
📲 Мы в MAX
👉@BookPython
zip может быть хорошим выбором. Она возвращает генератор, который выдаёт кортежи, содержащие по одному элементу из каждого исходного итерируемого объекта:
In : eng = ['one', 'two', 'three']
In : ger = ['eins', 'zwei', 'drei']
In : for e, g in zip(eng, ger):
...: print('{e} = {g}'.format(e=e, g=g))
...:
one = eins
two = zwei
three = drei
Обратите внимание, что
zip принимает итерируемые объекты как отдельные аргументы, а не список аргументов. Для распаковки значений можно использовать оператор
*:
In : list(zip(*zip(eng, ger)))
Out: [('one', 'two', 'three'), ('eins', 'zwei', 'drei')]
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Модуль
Эти два типа —
📲 Мы в MAX
👉@BookPython
io предоставляет два типа файловых объектов в памяти. Такие объекты могут быть полезны для работы с интерфейсами, которые поддерживают только файлы, без необходимости создавать их на диске. Очевидный пример — модульное тестирование.Эти два типа —
BytesIO и StringIO, которые работают соответственно с байтами и строками.
from io import StringIO
f = StringIO()
f.write('first\n') # Вывод: 6
f.write('second\n') # Вывод: 7
f.seek(0) # Вывод: 0
print(f.readline()) # Вывод: 'first\n'
print(f.readline()) # Вывод: 'second\n'
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
Python поддерживает несколько способов запуска скрипта. Обычный вариант — это
Однако, можно также использовать
Можно также запустить Python с каталогом:
Пример:
📲 Мы в MAX
👉@BookPython
python foo.py; в этом случае foo.py просто выполняется.Однако, можно также использовать
python -m foo. Если foo — это не пакет, то foo.py ищется в sys.path и выполняется. Если это пакет, то Python сначала выполняет foo/__init__.py, а затем foo/__main__.py. Обратите внимание, что во время выполнения __init__.py значение __name__ равно foo, но во время выполнения __main__.py оно равно __main__.Можно также запустить Python с каталогом:
python dir/ или даже python dir.zip. В этом случае Python ищет dir/__main__.py и выполняет его, если находит.Пример:
$ ls foo
__init__.py __main__.py
$ cat foo/__init__.py
print(__name__)
$ cat foo/__main__.py
print(__name__)
$ python -m foo
foo
__main__
$ python foo/
__main__
$ python foo/__init__.py
__main__
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3👎1
Оптимизация кода с помощью генераторов в Python
Сегодня хочу показать вам, как использование генераторов может сделать ваш код быстрее, экономнее по памяти и элегантнее.
Что такое генераторы?
Генераторы — это функции, которые используют
Пример: экономия памяти
Допустим, нам нужно обработать миллион чисел и взять из них только четные. Обычный способ:
Такой код загружает в память весь список, что может быть проблемой при больших данных.
А теперь переделаем на генератор:
Здесь список не создается, а элементы выдаются по одному. Это экономит память и ускоряет обработку!
Где применять?
✔️ Чтение больших файлов построчно (
✔️ Работа с потоками данных
✔️ Генерация последовательностей без создания списков
📲 Мы в MAX
👉@BookPython
Сегодня хочу показать вам, как использование генераторов может сделать ваш код быстрее, экономнее по памяти и элегантнее.
Что такое генераторы?
Генераторы — это функции, которые используют
yield вместо return. Они не возвращают сразу все значения, а запоминают своё состояние и отдают результат по мере необходимости. Это особенно полезно при обработке больших объемов данных, так как позволяет не загружать всю информацию в память сразу. Пример: экономия памяти
Допустим, нам нужно обработать миллион чисел и взять из них только четные. Обычный способ:
def get_even_numbers(n):
result = []
for i in range(n):
if i % 2 == 0:
result.append(i)
return result
numbers = get_even_numbers(10**6)
print(len(numbers)) # 500000
Такой код загружает в память весь список, что может быть проблемой при больших данных.
А теперь переделаем на генератор:
def get_even_numbers_gen(n):
for i in range(n):
if i % 2 == 0:
yield i
numbers = get_even_numbers_gen(10**6)
print(sum(1 for _ in numbers)) # 500000
Здесь список не создается, а элементы выдаются по одному. Это экономит память и ускоряет обработку!
Где применять?
✔️ Чтение больших файлов построчно (
yield line) ✔️ Работа с потоками данных
✔️ Генерация последовательностей без создания списков
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
Оптимизация SQL-запросов в Django ORM
Сегодня я покажу вам, как оптимизировать SQL-запросы в Django ORM, чтобы ваш код работал быстрее и эффективнее. Если ваш Django-проект начал тормозить, скорее всего, проблема в количестве и сложности запросов к базе данных.
1️⃣ Используйте
Django ORM лениво загружает связанные объекты, что может привести к множественным SQL-запросам (N+1). Вместо этого используйте:
Это значительно уменьшает количество запросов к базе.
2️⃣ Используйте
Если вам не нужны все поля модели, загружайте только необходимые:
А если хотите исключить несколько полей:
3️⃣ Агрегация вместо перебора в Python
Вместо:
Используйте
Это выполнится на стороне базы, а не в Python, что намного быстрее.
4️⃣ Используйте
Если вам нужно проверить, есть ли записи в базе, не используйте
❌ Плохо:
5️⃣ Кешируйте запросы
Django поддерживает кеширование, и если запросы повторяются, можно использовать:
Эти простые приемы помогут вам ускорить Django-приложение и уменьшить нагрузку на базу данных. А вы уже используете их в своих проектах? Делитесь в комментариях! 👇
📲 Мы в MAX
👉@BookPython
Сегодня я покажу вам, как оптимизировать SQL-запросы в Django ORM, чтобы ваш код работал быстрее и эффективнее. Если ваш Django-проект начал тормозить, скорее всего, проблема в количестве и сложности запросов к базе данных.
1️⃣ Используйте
select_related и prefetch_relatedDjango ORM лениво загружает связанные объекты, что может привести к множественным SQL-запросам (N+1). Вместо этого используйте:
# select_related — жадная загрузка (для ForeignKey, OneToOne)
posts = Post.objects.select_related("author").all()
# prefetch_related — для ManyToMany и Reverse ForeignKey
posts = Post.objects.prefetch_related("comments").all()
Это значительно уменьшает количество запросов к базе.
2️⃣ Используйте
only и deferЕсли вам не нужны все поля модели, загружайте только необходимые:
users = User.objects.only("id", "username") # Загружаем только ID и имя
А если хотите исключить несколько полей:
users = User.objects.defer("bio", "last_login") # Исключаем ненужные поля
3️⃣ Агрегация вместо перебора в Python
Вместо:
total_likes = sum(post.likes.count() for post in posts)
Используйте
annotate:
from django.db.models import Count
posts = Post.objects.annotate(total_likes=Count("likes"))
Это выполнится на стороне базы, а не в Python, что намного быстрее.
4️⃣ Используйте
exists() вместо count()Если вам нужно проверить, есть ли записи в базе, не используйте
count(), это дорогостоящий запрос:
if User.objects.filter(email="test@example.com").exists(): # Быстро
❌ Плохо:
if User.objects.filter(email="test@example.com").count() > 0: # Долго
5️⃣ Кешируйте запросы
Django поддерживает кеширование, и если запросы повторяются, можно использовать:
from django.core.cache import cache
users = cache.get("users")
if not users:
users = list(User.objects.all()) # Загружаем пользователей
cache.set("users", users, timeout=60 * 15) # Кешируем на 15 минут
Эти простые приемы помогут вам ускорить Django-приложение и уменьшить нагрузку на базу данных. А вы уже используете их в своих проектах? Делитесь в комментариях! 👇
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2❤1
Structured Concurrency в Python 3.11+ (TaskGroup vs Gather)
Если вы до сих пор используете
Главная проблема
В Python 3.11+ (и через
Как это выглядит:
Почему TaskGroup это выбор Middle+:
1. Атомарность скоупа: Если одна задача внутри контекстного менеджера падает,
2. Exception Groups: Если упало несколько задач одновременно (или во время отмены), Python соберет их в
Вывод:
Для скриптов
#asyncio #python311 #bestpractices #concurrency
📲 Мы в MAX
👉@BookPython
Если вы до сих пор используете
asyncio.gather() для запуска конкурентных задач, вы, вероятно, теряете контроль над ошибками.Главная проблема
gather: если одна таска падает, остальные продолжают работать (если не стоит return_exceptions=False, который убивает всё сразу, но "грязно"). Плюс, отлавливать несколько ошибок одновременно через try / except - это боль.В Python 3.11+ (и через
trio / anyio раньше) стандартом стала Structured Concurrency через asyncio.TaskGroup.Как это выглядит:
import asyncio
async def task_fail(name, sec):
await asyncio.sleep(sec)
raise ValueError(f"Error in {name}")
async def task_ok(name, sec):
await asyncio.sleep(sec)
print(f"Task {name} done")
async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(task_ok("A", 1))
tg.create_task(task_fail("B", 2)) # Упадет через 2 сек
tg.create_task(task_fail("C", 1.5)) # Упадет через 1.5 сек
except* ValueError as eg:
# Обратите внимание на except* (ExceptionGroup)
print(f"Caught errors: {eg.exceptions}")
# Запуск
# asyncio.run(main())
Почему TaskGroup это выбор Middle+:
1. Атомарность скоупа: Если одна задача внутри контекстного менеджера падает,
TaskGroup автоматически отменяет (cancel) все остальные запущенные задачи в группе. Вы не оставляете "зомби-процессы" в фоне.2. Exception Groups: Если упало несколько задач одновременно (или во время отмены), Python соберет их в
ExceptionGroup. Конструкция except* позволяет элегантно обрабатывать дерево исключений.Вывод:
Для скриптов
gather сойдет. Для надежных сервисов, где важна консистентность состояния и корректный шатдаун корутин - переезжайте на TaskGroup.#asyncio #python311 #bestpractices #concurrency
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2🔥2
Списковые включения (list comprehensions) могут содержать несколько операторов
Можно также добавлять условия
Любое выражение с
Вы можете комбинировать
📲 Мы в MAX
👉@BookPython
for и if:
In : [(x, y) for x in range(3) for y in range(3)]
Out: [
(0, 0), (0, 1), (0, 2),
(1, 0), (1, 1), (1, 2),
(2, 0), (2, 1), (2, 2)
]
Можно также добавлять условия
if для фильтрации значений:
In : [
(x, y)
for x in range(3)
for y in range(3)
if x != 0
if y != 0
]
Out: [(1, 1), (1, 2), (2, 1), (2, 2)]
Любое выражение с
for и if может использовать все переменные, определённые ранее:
In : [
(x, y)
for x in range(3)
for y in range(x + 2)
if x != y
]
Out: [
(0, 1),
(1, 0), (1, 2),
(2, 0), (2, 1), (2, 3)
]
Вы можете комбинировать
for и if в любом порядке:
In : [
(x, y)
for x in range(5)
if x % 2
for y in range(x + 2)
if x != y
]
Out: [
(1, 0), (1, 2),
(3, 0), (3, 1), (3, 2), (3, 4)
]
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5😁1
🦾 Чем реально отличается Senior ML от уверенного Middle?
На открытом вебинаре разберём продвинутые методы машинного обучения, о которых обычно говорят вскользь или не говорят вовсе. Рекомендательные системы, временные ряды, reinforcement learning, GenAI, байесовские подходы и деплой моделей в прод: где и зачем они применяются сегодня и почему без них невозможно расти дальше в профессии.
Это честный разговор о том, какие навыки действительно ценятся на уровне Senior, какие задачи решают специалисты ML Advanced и какие карьерные возможности открываются после перехода на эту ступень. Вы сможете задать любые вопросы и получить ответы от эксперта.
📋 Встречаемся 28 января в 18:00 МСК в преддверии старта курса «Machine Learning. Advanced». Регистрация открыта: https://vk.cc/cTo0ZX
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
На открытом вебинаре разберём продвинутые методы машинного обучения, о которых обычно говорят вскользь или не говорят вовсе. Рекомендательные системы, временные ряды, reinforcement learning, GenAI, байесовские подходы и деплой моделей в прод: где и зачем они применяются сегодня и почему без них невозможно расти дальше в профессии.
Это честный разговор о том, какие навыки действительно ценятся на уровне Senior, какие задачи решают специалисты ML Advanced и какие карьерные возможности открываются после перехода на эту ступень. Вы сможете задать любые вопросы и получить ответы от эксперта.
📋 Встречаемся 28 января в 18:00 МСК в преддверии старта курса «Machine Learning. Advanced». Регистрация открыта: https://vk.cc/cTo0ZX
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
❤1
Pydantic V2: Забываем
Переход на Pydantic V2, это не только ускорение за счет ядра на Rust, но и переосмысление валидации. Самая частая боль при миграции, проверка зависимостей между несколькими полями.
В V1 мы использовали
В чем соль?
В режиме
Пример (валидация периода дат):
Нюансы для профи:
1.
2. Производительность: Валидаторы на Python, это узкое горлышко. Если у вас HighLoad, старайтесь выразить ограничения через
Используйте возможности типизации на 100%.
#pydantic #fastapi #bestpractices #python
📲 Мы в MAX
👉@BookPython
root_validator, используем model_validator правильноПереход на Pydantic V2, это не только ускорение за счет ядра на Rust, но и переосмысление валидации. Самая частая боль при миграции, проверка зависимостей между несколькими полями.
В V1 мы использовали
root_validator и работали со словарем values (прощай, автодополнение IDE). В V2 правильный путь - model_validator в режиме after.В чем соль?
В режиме
mode='after' валидация запускается после того, как поля были распаршены и приведены к типам. Вы работаете с экземпляром класса (self), а не с сырым словарем.Пример (валидация периода дат):
from pydantic import BaseModel, model_validator
from datetime import datetime
class DateRange(BaseModel):
start_dt: datetime
end_dt: datetime
@model_validator(mode='after')
def check_dates_order(self):
# Обращаемся через self — IDE видит поля и их типы!
if self.end_dt <= self.start_dt:
raise ValueError("Дата окончания должна быть позже начала")
return self
# Тест
try:
DateRange(
start_dt="2024-01-01T12:00:00",
end_dt="2023-01-01T12:00:00"
)
except ValueError as e:
print(e)
Нюансы для профи:
1.
mode='before': Используйте только если вам нужно модифицировать сырые входные данные (например, JSON) до того, как Pydantic начнет их парсить. Это аналог pre=True из V1.2. Производительность: Валидаторы на Python, это узкое горлышко. Если у вас HighLoad, старайтесь выразить ограничения через
Field (например, ge, le), так как они отрабатывают на Rust-уровне, что значительно быстрее вызова python-функции.Используйте возможности типизации на 100%.
#pydantic #fastapi #bestpractices #python
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🐍 Как улучшить читаемость кода в Python?
Читаемый код - это не роскошь, а необходимость. Если ваш код трудно понять, даже если он работает, это плохой код. Сегодня разберем несколько простых, но мощных приемов, которые сделают ваш код более понятным.
1️⃣ Используйте говорящие имена переменных
Плохой пример:
Хороший пример:
Теперь сразу понятно, что делает код!
2️⃣ Разбивайте код на функции
Вместо длинных кусков кода, используйте функции:
Теперь код можно переиспользовать и проще тестировать.
3️⃣ Следуйте PEP 8
Форматирование кода влияет на его читаемость. Например, пробелы вокруг операторов делают код более понятным:
Пользуйтесь
4️⃣ Избегайте магических чисел
Если в коде встречаются непонятные числа, лучше заменить их на константы:
5️⃣ Используйте list comprehensions
Вместо:
Лучше:
Чище и лаконичнее!
Читаемый код делает разработку приятнее, ускоряет исправление багов и упрощает поддержку. Напишите в комментариях, какие еще приемы вы используете для улучшения читаемости кода! 👇
📲 Мы в MAX
👉@BookPython
Читаемый код - это не роскошь, а необходимость. Если ваш код трудно понять, даже если он работает, это плохой код. Сегодня разберем несколько простых, но мощных приемов, которые сделают ваш код более понятным.
1️⃣ Используйте говорящие имена переменных
Плохой пример:
a = 10
b = 20
c = a + b
Хороший пример:
price = 10
tax = 20
total_cost = price + tax
Теперь сразу понятно, что делает код!
2️⃣ Разбивайте код на функции
Вместо длинных кусков кода, используйте функции:
def calculate_total(price, tax):
return price + tax
total_cost = calculate_total(10, 20)
Теперь код можно переиспользовать и проще тестировать.
3️⃣ Следуйте PEP 8
Форматирование кода влияет на его читаемость. Например, пробелы вокруг операторов делают код более понятным:
# Плохо
total=price+tax
# Хорошо
total = price + tax
Пользуйтесь
black или flake8, чтобы следить за стилем.4️⃣ Избегайте магических чисел
Если в коде встречаются непонятные числа, лучше заменить их на константы:
# Плохо
if age > 18:
print("Взрослый")
# Хорошо
LEGAL_AGE = 18
if age > LEGAL_AGE:
print("Взрослый")
5️⃣ Используйте list comprehensions
Вместо:
numbers = [1, 2, 3, 4, 5]
squared_numbers = []
for num in numbers:
squared_numbers.append(num ** 2)
Лучше:
squared_numbers = [num ** 2 for num in numbers]
Чище и лаконичнее!
Читаемый код делает разработку приятнее, ускоряет исправление багов и упрощает поддержку. Напишите в комментариях, какие еще приемы вы используете для улучшения читаемости кода! 👇
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤1
📌 Декораторы в Python: как они работают и зачем нужны?
Сегодня я покажу вам, как работают декораторы в Python и зачем они вообще нужны.
Декораторы позволяют изменять поведение функций без изменения их кода. Они широко применяются в логировании, кешировании, управлении доступом и многом другом.
Допустим, у нас есть функция, которая просто выводит «Hello, world!»:
Теперь мы хотим, чтобы перед выполнением этой функции выполнялся какой-то код, например, логирование. Вместо изменения
И теперь используем его:
👉 Вывод:
Как это работает?
1. Декоратор принимает функцию (
2. Внутри создаётся вложенная функция
3.
Можно даже передавать аргументы в декорируемую функцию:
👉 Вывод:
🔥 Декораторы это мощный инструмент, который делает код чище и удобнее. Если ещё не использовали их в проектах, самое время попробовать!
А какие декораторы вы используете в своих проектах? Делитесь в комментариях! ⬇️
📲 Мы в MAX
👉@BookPython
Сегодня я покажу вам, как работают декораторы в Python и зачем они вообще нужны.
Декораторы позволяют изменять поведение функций без изменения их кода. Они широко применяются в логировании, кешировании, управлении доступом и многом другом.
Допустим, у нас есть функция, которая просто выводит «Hello, world!»:
def greet():
print("Hello, world!")
Теперь мы хотим, чтобы перед выполнением этой функции выполнялся какой-то код, например, логирование. Вместо изменения
greet(), мы создадим декоратор:
def log_decorator(func):
def wrapper():
print(f"Вызов функции {func.__name__}")
return func()
return wrapper
И теперь используем его:
@log_decorator
def greet():
print("Hello, world!")
greet()
👉 Вывод:
Вызов функции greet
Hello, world!
Как это работает?
1. Декоратор принимает функцию (
func) в качестве аргумента.2. Внутри создаётся вложенная функция
wrapper(), которая выполняет дополнительную логику перед вызовом func().3.
wrapper() возвращается вместо func, фактически подменяя её.Можно даже передавать аргументы в декорируемую функцию:
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"Вызов {func.__name__} с аргументами: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_decorator
def add(a, b):
return a + b
print(add(3, 5))
👉 Вывод:
Вызов add с аргументами: (3, 5), {}
8
🔥 Декораторы это мощный инструмент, который делает код чище и удобнее. Если ещё не использовали их в проектах, самое время попробовать!
А какие декораторы вы используете в своих проектах? Делитесь в комментариях! ⬇️
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍1👎1
Локальное окружение для начинающего ML-инженера
ML начинается с окружения. Разберём настройку Python-окружения, виртуальных сред, Jupyter и VS Code, а также структуру ML-проекта и управление зависимостями.
📌 22 января в 18:00 МСК Открытый урок курса «Machine Learning»
Зарегистрироваться: https://vk.cc/cTC0sV
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
ML начинается с окружения. Разберём настройку Python-окружения, виртуальных сред, Jupyter и VS Code, а также структуру ML-проекта и управление зависимостями.
Зарегистрироваться: https://vk.cc/cTC0sV
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2