Недавно я начал систематизировать практики которые обычно используются и помогают экономить время. Я хочу поделиться некоторыми из них.

1. Явное состояние и мемоизация

Скрытые состояния в замыканиях и декораторах часто приводят к трудноуловимым багам.

from functools import wraps

def memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

Использование @wraps сохраняет имя функции, docstring и метаданные — критично для дебага и интеграции с Flask.

2. Асинхронность для продакшн

Асинхронность часто ухудшает код, если использовать её неправильно.

import asyncio, aiohttp

TOTAL_REQUESTS = 1_000_000
sem = asyncio.Semaphore(1000)

async def fetch(session, url):
    async with sem:
        try:
            async with session.get(url) as resp:
                return await resp.text()
        except aiohttp.ClientError:
            return None
          
async def main():
    urls = [f"https://api.site/{i}" for i in range(TOTAL_REQUESTS)]
    async with aiohttp.ClientSession() as session:
        for i in range(0, len(urls), 10_000):
            chunk = urls[i:i+10_000]
            tasks = [fetch(session, url) for url in chunk]
            await asyncio.gather(*tasks)

Контроль через Semaphore + чанки предотвращает OOM и блокировки API. Используйте create_task() для управления жизненным циклом корутин.

3. Ошибки и raise

Не ловите всё подряд и используйте новые возможности языка.

# Python 3.11+
user.is_admin or raise PermissionError("Not allowed!")

raise как выражение и except* (Python 3.11) делают обработку ошибок лаконичной и безопасной:

# Python 3.11+
try:
    await asyncio.gather(fail1(), fail2())
except* ValueError as ve:  # Только ValueError
    print(f"ValueErrors: {ve.exceptions}")
except* TypeError as te:   # Только TypeError
    print(f"TypeErrors: {te.exceptions}")

4. Типизация и валидация

from pydantic import validate_call
from typing import Annotated
from pydantic.types import Gt, Ge, Le 

@validate_call
def calculate_discount(
        price: Annotated[float, Gt(0)],
        discount: Annotated[float, Ge(0), Le(100)]
) -> float:
    return price * (1 - discount / 100) 

Constraints прямо в аннотациях делают сигнатуры самодокументируемыми и безопасными без дублирования проверок.

5. Ленивая загрузка и кеширование

@cached_property и functools.cache экономят время при дорогих вычислениях.

from functools import cached_property
import time

class UserReport:
    def __init__(self, user_id):
        self.user_id = user_id        
    @cached_property
    def total_spent(self):
        print("Querying database...")
        time.sleep(2)  # expensive call
        return 199.99
      
r = UserReport(123)
print(r.total_spent)     # computed once
print(r.total_spent)     # cached instantly
del r.__dict__["total_spent"]
print(r.total_spent)     # recomputed after cache reset

Результат хранится в dict объекта, можно сбросить при необходимости. Отлично подходит для API-запросов и конфигураций.

6. Python 3.14+

Новые возможности языка ускоряют работу и упрощают код:

  • uuid7() — уникальные и сортируемые по времени ключи

  • ContextVar как контекстный менеджер

  • t-strings (t"...") для отложенных шаблонов

  • subTests для granular тестирования

from string.templatelib import Template

def render(template: Template):
    parts = []
    for item in template:
        if isinstance(item, str):
            parts.append(item)
    return "".join(parts)

user = "Alice"
role = "admin"
t = t"user: {user} — role: {role}"
s = render(t)

Жалко что не добавили .format() для t -строк

Заключение

Даже небольшие изменения в подходе к разработке дают ощутимый эффект на качество кода и скорость разработки.

Комментарии (6)


  1. AlexZander116
    13.11.2025 07:58

    Самодокументируемыми вроде слитно пишется. Иначе на финский язык смахивает очень)


    1. robomania Автор
      13.11.2025 07:58

      спасибо, исправил


  1. ilmix007
    13.11.2025 07:58

    Мне для лайка кармы не хватает, но коммент пропукает.
    Спасибо.


    1. ilmix007
      13.11.2025 07:58

      *пропускает


  1. vityo
    13.11.2025 07:58

    Смотрю на эти бесконечные фичи и думаю. Слишком строгий язык - сложно написать, слишком свободный - тяжело прочитать. Вот бы где-то остановились уже. Этих вещей реально может быть бесконечно, и каждая "полезная" . Запретить свободу выражения - развития не будет. В общем, начинали бы тогда запрещать что ли какие-то старые конструкции хотя бы, т.е. меньше обратной этой совместимости бы что ли с каждой новой версией бы. Потому что невозможно просто. Молодым зашедшим норм, они могут старого не знать (или наоборот погано - заставят учить), а старым тяжело - опять новое учить (и легко, старый код не надо переписывать).

    Короче походу скорость развития программных языков будет как развитие человеческих - зависеть от длины жизни людей...


    1. robomania Автор
      13.11.2025 07:58

      Мне кажется, Python пытается идти путем "разумных defaults" и постепенного внедрения. Например, @dataclass или cached_property — это не усложнение, а, наоборот, упрощение, убирающее тонны шаблонного кода.

      Насчет обратной совместимости — здесь палка о двух концах.

      Метафора о длине жизни точна, прям задумался...