Создание текстовых отчетов и логов с записью в файл день за днем
Рано или поздно любой скрипт вырастает до состояния: «Хочу понимать, что он делал вчера ночью». Тут на сцену выходят отчеты и логи. Давайте разберем пару рабочих приемов: ежедневные файлы логов и простые текстовые отчеты.
---
## Ежедневный лог-файл
Частая практика — один файл лога на день:
Сделаем простую функцию-логгер:
Что здесь важно:
-
- Режим
- Имя файла зависит от даты — архив логов сам формируется по дням.
---
## Простой текстовый отчет по результатам работы
Представим скрипт, который что-то обрабатывает и в конце дня создает итоговый отчет: сколько задач прошло, сколько упало, какой процент успеха.
Фишки:
- Отчет — обычный текстовый файл, его легко открыть в любом редакторе или отправить по почте.
- Формат предельно простой, но уже дает картину дня.
- Логгер и отчет связаны: лог фиксирует факт создания отчета.
---
## Идея для развития
Дальше можно:
- добавлять в отчет «ТОП-5 ошибок дня»;
- архивировать старые логи в zip;
- использовать модуль
Но фундамент один: аккуратная, ежедневная запись в файлы с понятными именами и структурой. Это уже делает ваш код ближе к «боевому» уровню.
Рано или поздно любой скрипт вырастает до состояния: «Хочу понимать, что он делал вчера ночью». Тут на сцену выходят отчеты и логи. Давайте разберем пару рабочих приемов: ежедневные файлы логов и простые текстовые отчеты.
---
## Ежедневный лог-файл
Частая практика — один файл лога на день:
log_2025-01-21.txt, log_2025-01-22.txt и т.д. Сделаем простую функцию-логгер:
from datetime import datetime
from pathlib import Path
LOG_DIR = Path("logs")
LOG_DIR.mkdir(exist_ok=True)
def get_log_file_path() -> Path:
today_str = datetime.now().strftime("%Y-%m-%d")
return LOG_DIR / f"log_{today_str}.txt"
def log_message(level: str, message: str) -> None:
log_file = get_log_file_path()
time_str = datetime.now().strftime("%H:%M:%S")
line = f"[{time_str}] [{level.upper()}] {message}\n"
with log_file.open("a", encoding="utf-8") as f:
f.write(line)
# Пример использования
log_message("info", "Script started")
log_message("warning", "Low disk space")
log_message("error", "Failed to connect to server")
Что здесь важно:
-
Path из pathlib удобнее обычных строк для путей.- Режим
"a" — дозапись в конец файла, не стирая старые логи.- Имя файла зависит от даты — архив логов сам формируется по дням.
---
## Простой текстовый отчет по результатам работы
Представим скрипт, который что-то обрабатывает и в конце дня создает итоговый отчет: сколько задач прошло, сколько упало, какой процент успеха.
from datetime import datetime
from pathlib import Path
REPORT_DIR = Path("reports")
REPORT_DIR.mkdir(exist_ok=True)
def save_daily_report(total: int, success: int, failed: int) -> Path:
today_str = datetime.now().strftime("%Y-%m-%d")
report_path = REPORT_DIR / f"report_{today_str}.txt"
success_rate = (success / total * 100) if total else 0
lines = [
f"Report date: {today_str}",
f"Total tasks: {total}",
f"Successful: {success}",
f"Failed: {failed}",
f"Success rate: {success_rate:.2f}%",
]
with report_path.open("w", encoding="utf-8") as f:
f.write("\n".join(lines))
return report_path
# Пример
report_file = save_daily_report(total=120, success=110, failed=10)
log_message("info", f"Daily report saved to {report_file}")
Фишки:
- Отчет — обычный текстовый файл, его легко открыть в любом редакторе или отправить по почте.
- Формат предельно простой, но уже дает картину дня.
- Логгер и отчет связаны: лог фиксирует факт создания отчета.
---
## Идея для развития
Дальше можно:
- добавлять в отчет «ТОП-5 ошибок дня»;
- архивировать старые логи в zip;
- использовать модуль
logging, чтобы прокачать формат и уровни логов.Но фундамент один: аккуратная, ежедневная запись в файлы с понятными именами и структурой. Это уже делает ваш код ближе к «боевому» уровню.
👍3
Изучение
Модуль
Разберём три ключевые группы: комбинации, перестановки и бесконечные итераторы.
---
## Комбинации и перестановки
###
Результат:
Полезно для перебора возможных пар/наборов параметров, команд, вариантов выбора.
###
Здесь и
###
Это быстрый способ перебрать все сочетания параметров при тестировании, конфигурациях и т.п.
---
## Бесконечные итераторы
Иногда удобно иметь “бесконечный источник” данных, который вы сами ограничиваете.
###
###
Получаем повторяющийся цикл состояний, полезно для простых симуляций или циклических индикаторов.
###
В связке с
---
itertools: комбинации, перестановки и бесконечные итераторыМодуль
itertools — это чемоданчик с инструментами для работы с последовательностями. Он не шумит, не требует сложной настройки, но иногда одним-двумя его вызовами можно заменить десяток строк кода с циклами.Разберём три ключевые группы: комбинации, перестановки и бесконечные итераторы.
---
## Комбинации и перестановки
###
combinationscombinations(iterable, r) перебирает все уникальные наборы по r элементов без повторов и без учёта порядка.from itertools import combinations
items = ['a', 'b', 'c', 'd']
for combo in combinations(items, 2):
print(combo)
Результат:
('a', 'b'), ('a', 'c'), ... — но не будет ('b', 'a'), потому что порядок не важен.Полезно для перебора возможных пар/наборов параметров, команд, вариантов выбора.
###
permutationspermutations(iterable, r=None) — все возможные варианты с учётом порядка.from itertools import permutations
letters = ['A', 'B', 'C']
for p in permutations(letters, 2):
print(p)
Здесь и
('A', 'B'), и ('B', 'A') — уже разные результаты. Удобно, когда порядок критичен: генерация пароля, маршрута, очередности задач.###
productproduct — декартово произведение. Можно представить как вложенные циклы.from itertools import product
colors = ['red', 'green']
sizes = ['S', 'M', 'L']
for item in product(colors, sizes):
print(item)
Это быстрый способ перебрать все сочетания параметров при тестировании, конфигурациях и т.п.
---
## Бесконечные итераторы
Иногда удобно иметь “бесконечный источник” данных, который вы сами ограничиваете.
###
countcount(start=0, step=1) — бесконечный счётчик.from itertools import count, islice
for n in islice(count(10, 2), 5):
print(n)
islice здесь обрезает бесконечную последовательность до 5 элементов: 10, 12, 14, 16, 18.###
cyclecycle(iterable) — крутит последовательность по кругу.from itertools import cycle, islice
states = ['loading', 'processing', 'done']
for s in islice(cycle(states), 7):
print(s)
Получаем повторяющийся цикл состояний, полезно для простых симуляций или циклических индикаторов.
###
repeatrepeat(object, times=None) возвращает один и тот же объект много раз.from itertools import repeat
for msg in repeat("ping", 3):
print(msg)
В связке с
map удобно передавать одинаковый аргумент множеству вызовов.---
itertools хорош тем, что он “мысленно сокращает” код: когда вы видите combinations или product, сразу понятно, что именно происходит, без чтения вложенных циклов. Попробуйте заменить пару своих циклов этими функциями — и код станет и короче, и чище.👍3
Pathlib: современный способ подружиться с путями и файлами в Python
Модуль
---
### Создание путей
Оператор
---
### Проверка существования и свойств
---
### Чтение и запись файлов
Для текстовых файлов
Для бинарных данных есть методы
---
### Обход директорий и поиск файлов
---
### Безопасные операции с файлами
---
Модуль
pathlib — это попытка Python сказать: «Хватит мучиться со строками путей и os.path». Он даёт удобный объект Path, который понимает операционные системы, красиво соединяет части пути и умеет работать с файлами почти как с объектами.---
### Создание путей
from pathlib import Path
# Текущая директория
current_dir = Path.cwd()
# Домашняя директория
home_dir = Path.home()
# Относительный путь
project_dir = Path("projects") / "my_app"
# Путь с "магическим" слэшем
config_file = project_dir / "config.yaml"
print(config_file) # projects/my_app/config.yaml (или с \ на Windows)
Оператор
/ — это не деление, а аккуратное склеивание частей пути под вашу ОС.---
### Проверка существования и свойств
from pathlib import Path
path = Path("data/example.txt")
print(path.exists()) # файл или папка существует?
print(path.is_file()) # это файл?
print(path.is_dir()) # это директория?
print(path.parent) # родительская директория
print(path.name) # имя файла с расширением
print(path.stem) # имя без расширения
print(path.suffix) # расширение (.txt)
---
### Чтение и запись файлов
Для текстовых файлов
pathlib даёт очень лаконичный синтаксис:from pathlib import Path
text_file = Path("notes/todo.txt")
# Запись
text_file.parent.mkdir(parents=True, exist_ok=True)
text_file.write_text("Learn pathlib\nUse it everywhere!", encoding="utf-8")
# Чтение
content = text_file.read_text(encoding="utf-8")
print(content)
Для бинарных данных есть методы
write_bytes() и read_bytes().---
### Обход директорий и поиск файлов
from pathlib import Path
logs_dir = Path("logs")
# Все .log-файлы в директории (без поддиректорий)
for log_file in logs_dir.glob("*.log"):
print(log_file)
# Рекурсивно найти все .py-файлы
for py_file in Path(".").rglob("*.py"):
print(py_file)
glob() и rglob() понимают шаблоны (*, ?), так что можно быстро фильтровать нужные файлы.---
### Безопасные операции с файлами
from pathlib import Path
src = Path("data/raw/data.csv")
dst = Path("data/processed/data.csv")
# Создаём директорию, если её нет
dst.parent.mkdir(parents=True, exist_ok=True)
# Переименование или перенос
if src.exists():
src.rename(dst)
---
pathlib заменяет кучу разрозненных функций из os, os.path и shutil единым, логичным интерфейсом. Стоит привыкнуть к Path — и строковые пути начнут казаться пережитком прошлого.👍5
Работа с модулем
Если вы когда‑нибудь пытались “вручную” вычислить день недели или красиво вывести календарь в консоль, то модуль
### Быстрый старт: печатаем календарь месяца
Результат — готовый текстовый календарь марта 2025. Для целого года есть аналог:
Уже можно делать простенькие консольные планировщики.
### Как устроен месяц “внутри”
Чтобы работать с датами программно, удобен метод
Каждая неделя — список из 7 чисел. Нули означают “нет дня” (ячейка принадлежит соседнему месяцу). Это удобно для логики, например, подсветки выходных или праздников.
### Добавляем события в календарь
Сделаем простую текстовую “раскраску” дат с событиями:
Вы увидите сетку чисел, а дни с событиями будут помечены
### Дни недели и “рабочий” календарь
Модуль
Так можно быстро считать количество рабочих дней, искать все пятницы или строить графики дежурств.
### Дополнительно
-
-
-
calendar: удобное отображение дат и событийЕсли вы когда‑нибудь пытались “вручную” вычислить день недели или красиво вывести календарь в консоль, то модуль
calendar создан именно для того, чтобы вы перестали страдать.### Быстрый старт: печатаем календарь месяца
import calendar
year = 2025
month = 3
cal = calendar.month(year, month)
print(cal)
Результат — готовый текстовый календарь марта 2025. Для целого года есть аналог:
print(calendar.calendar(2025))
Уже можно делать простенькие консольные планировщики.
### Как устроен месяц “внутри”
Чтобы работать с датами программно, удобен метод
monthcalendar:import calendar
year = 2025
month = 3
c = calendar.Calendar(firstweekday=0) # 0 – понедельник
month_matrix = c.monthdayscalendar(year, month)
for week in month_matrix:
print(week)
Каждая неделя — список из 7 чисел. Нули означают “нет дня” (ячейка принадлежит соседнему месяцу). Это удобно для логики, например, подсветки выходных или праздников.
### Добавляем события в календарь
Сделаем простую текстовую “раскраску” дат с событиями:
import calendar
events = {
(2025, 3, 8): "Holiday",
(2025, 3, 15): "Deadline",
}
year, month = 2025, 3
c = calendar.Calendar(firstweekday=0)
for week in c.monthdayscalendar(year, month):
line = []
for day in week:
if day == 0:
line.append(" ")
continue
key = (year, month, day)
if key in events:
# отмечаем события звездочкой
line.append(f"{day:2d}*")
else:
line.append(f"{day:2d} ")
print(" ".join(line))
Вы увидите сетку чисел, а дни с событиями будут помечены
*. Такой вывод легко адаптировать под любой интерфейс: консоль, web, GUI.### Дни недели и “рабочий” календарь
Модуль
calendar знает всё о буднях и выходных:import calendar
year, month = 2025, 3
workdays = []
for week in calendar.monthcalendar(year, month):
for i, day in enumerate(week):
if day == 0:
continue
# 0–4: понедельник–пятница
if i < 5:
workdays.append(day)
print("Workdays:", workdays)
Так можно быстро считать количество рабочих дней, искать все пятницы или строить графики дежурств.
### Дополнительно
-
calendar.isleap(year) — проверка високосного года. -
calendar.weekday(year, month, day) — номер дня недели (0 — понедельник). -
setfirstweekday() — глобально изменить первый день недели.calendar — отличный инструмент, чтобы перестать думать о том, на какой день недели падает 1 апреля, и сосредоточиться на логике приложения.👍5
Создание и использование генераторов:
Когда цикл
### Что такое генератор?
Генератор — это объект, который «помнит», где он остановился, и при следующем запросе продолжает с этого места. Создать его можно двумя способами:
1. Функция с ключевым словом
2. Генераторное выражение:
### Простейший пример с
Функция
### Генератор против списка
Оба варианта дадут одинаковый результат, но
### Бесконечные последовательности
Генераторы легко порождают бесконечные последовательности — такое невозможно в виде обычного списка:
Функция никогда не завершится сама, но генератор выдает значения, пока вы их запрашиваете.
### Генераторные выражения
Краткая запись:
Здесь
---
yield в действииКогда цикл
for в Python перебирает список, он хранит весь список в памяти. Удобно, но не всегда эффективно. Генераторы позволяют «лениво» выдавать значения по одному — экономя память и иногда ускоряя код.### Что такое генератор?
Генератор — это объект, который «помнит», где он остановился, и при следующем запросе продолжает с этого места. Создать его можно двумя способами:
1. Функция с ключевым словом
yield2. Генераторное выражение:
(x * 2 for x in range(10))### Простейший пример с
yielddef countdown(n):
while n > 0:
yield n
n -= 1
for number in countdown(5):
print(number)
Функция
countdown не возвращает список. Она каждый раз «выдает» следующее значение через yield. Память тратится только на текущее состояние, а не на весь набор чисел.### Генератор против списка
def squares_list(n):
result = []
for i in range(n):
result.append(i * i)
return result
def squares_gen(n):
for i in range(n):
yield i * i
print(sum(squares_list(10_000_000)))
print(sum(squares_gen(10_000_000)))
Оба варианта дадут одинаковый результат, но
squares_list создаст огромный список в памяти, а squares_gen будет считать по одному значению. Разница станет особенно заметна при больших n или при обработке потоков данных (логов, файлов, сетевых запросов).### Бесконечные последовательности
Генераторы легко порождают бесконечные последовательности — такое невозможно в виде обычного списка:
def infinite_counter(start=0):
current = start
while True:
yield current
current += 1
counter = infinite_counter()
for _ in range(5):
print(next(counter))
Функция никогда не завершится сама, но генератор выдает значения, пока вы их запрашиваете.
### Генераторные выражения
Краткая запись:
evens = (x for x in range(100) if x % 2 == 0)
print(sum(evens))
Здесь
evens — генератор, а не список. Память используется минимально.---
yield — это не просто «альтернатива return». Это способ думать о данных как о потоке значений: не «хранить всё», а «выдавать по запросу». Для обработки больших объемов данных и написания эффективного кода в Python это один из ключевых инструментов.👍5
### Модуль
Когда ваш код “летает”, это приятно. Но иногда нужно не только скорость, но и… паузы. Звучит странно, но без задержек и точного измерения времени не обойтись: от тестирования до симуляции медленных внешних сервисов.
Разберём модуль
---
## Базовая задержка:
Функция
Полезно для:
- имитации долгих операций (запросов к БД, API),
- создания задержек между запросами (во избежание банов),
- тестирования поведения интерфейсов.
---
## Измеряем время выполнения кода
Наивный способ —
Но у этого подхода есть минус: точность зависит от системных часов, которые могут “прыгать”.
---
## Более точный замер:
Для профилирования (измерения скорости) лучше использовать
Используйте
---
## Имитируем “медленный” внешний сервис
Представьте, что вы пишете функцию, которая обращается к API, но настоящего API ещё нет. Можно сделать фейковую задержку:
Так удобно тестировать:
- обработку долгих ответов,
- таймауты,
- индикаторы загрузки.
---
## Мини-профайлер на коленке
Можно написать простый декоратор для измерения времени выполнения любой функции.
Так вы быстро находите “узкие места” без тяжёлых инструментов.
---
Модуль
time: замер и имитация задержек в кодеКогда ваш код “летает”, это приятно. Но иногда нужно не только скорость, но и… паузы. Звучит странно, но без задержек и точного измерения времени не обойтись: от тестирования до симуляции медленных внешних сервисов.
Разберём модуль
time: как измерять выполнение кода и как “тормозить” программу по собственному желанию.---
## Базовая задержка:
time.sleepФункция
sleep просто приостанавливает выполнение программы на указанное число секунд (можно дробное).import time
print("Start")
time.sleep(2.5) # пауза 2.5 секунды
print("End")
Полезно для:
- имитации долгих операций (запросов к БД, API),
- создания задержек между запросами (во избежание банов),
- тестирования поведения интерфейсов.
---
## Измеряем время выполнения кода
Наивный способ —
time.time(). Он возвращает текущее время в секундах с начала эпохи (обычно 01.01.1970). Разница между двумя вызовами — затраченное время.import time
start = time.time()
result = sum(range(10_000_000))
end = time.time()
print(f"Result: {result}")
print(f"Elapsed: {end - start:.4f} seconds")
Но у этого подхода есть минус: точность зависит от системных часов, которые могут “прыгать”.
---
## Более точный замер:
time.perf_counterДля профилирования (измерения скорости) лучше использовать
perf_counter(). Это монотонный счётчик: не зависит от системного времени и даёт максимальную доступную точность.import time
start = time.perf_counter()
data = [x ** 2 for x in range(1_000_000)]
end = time.perf_counter()
print(f"Elapsed: {end - start:.6f} seconds")
Используйте
perf_counter везде, где важна точность измерений, особенно в тестах производительности.---
## Имитируем “медленный” внешний сервис
Представьте, что вы пишете функцию, которая обращается к API, но настоящего API ещё нет. Можно сделать фейковую задержку:
import time
import random
def fake_api_call():
delay = random.uniform(0.5, 1.5)
time.sleep(delay)
return {"status": "ok", "delay": delay}
start = time.perf_counter()
response = fake_api_call()
end = time.perf_counter()
print(response)
print(f"Real elapsed: {end - start:.3f} seconds")
Так удобно тестировать:
- обработку долгих ответов,
- таймауты,
- индикаторы загрузки.
---
## Мини-профайлер на коленке
Можно написать простый декоратор для измерения времени выполнения любой функции.
import time
from functools import wraps
def timeit(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} took {end - start:.5f} seconds")
return result
return wrapper
@timeit
def slow_function():
time.sleep(1)
return "done"
slow_function()
Так вы быстро находите “узкие места” без тяжёлых инструментов.
---
Модуль
time кажется простым, но это мощный инструмент: с ним вы можете и измерять производительность, и контролировать темп выполнения программы. А это уже шаг от “просто работает” к “работает предсказуемо и управляемо”.👍3❤1🔥1🤩1
Модуль
Когда в коде появляются числа вроде
---
### Проблема “магических значений”
Представим статус заказа:
Через пару недель вы уже не помните, что означает
---
### Enum: строгие и говорящие значения
Модуль
Плюсы:
- читаемость:
- защита от опечаток:
- автодополнение в IDE.
---
### Сравнение и хранение
Enum-члены уникальны:
Для хранения в БД удобно использовать
Если значение некорректно, будет
---
### Автоматические значения с
Когда вам не важны конкретные числа:
Значения будут 1, 2, 3 — но вам все равно, вы работаете только с именами.
---
### Enum как замена строковым константам
Строки тоже легко перепутать:
Вместо этого:
Теперь неправильное значение просто так не проскочит.
---
### Немного безопасности и порядка
Enum — это:
- централизованный список допустимых значений;
- самодокументирующийся код;
- меньше скрытых багов из-за опечаток и “магических чисел”.
Если вы пишете игру с типами юнитов, веб‑приложение со статусами заказов или API с ролями,
enum: читаемые и безопасные константы вместо “магических чисел”Когда в коде появляются числа вроде
1, 2, 3 с неочевидным смыслом, проект начинает расползаться на баги. Сегодня — про модуль enum, который превращает такие “магические числа” в понятные и безопасные константы.---
### Проблема “магических значений”
Представим статус заказа:
status = 2 # что это? "отправлен"? "отменен"?
Через пару недель вы уже не помните, что означает
2. А при рефакторинге можно легко перепутать значения.---
### Enum: строгие и говорящие значения
Модуль
enum позволяет задать набор именованных констант:from enum import Enum
class OrderStatus(Enum):
NEW = 1
IN_PROGRESS = 2
DONE = 3
status = OrderStatus.NEW
if status == OrderStatus.DONE:
print("Order completed")
Плюсы:
- читаемость:
OrderStatus.DONE понятнее, чем 3;- защита от опечаток:
OrderStatus.DNOE вызовет ошибку, а не тихо примет неверное число;- автодополнение в IDE.
---
### Сравнение и хранение
Enum-члены уникальны:
OrderStatus.NEW == OrderStatus.NEW # True
OrderStatus.NEW == OrderStatus.DONE # False
print(status.name) # NEW
print(status.value) # 1
Для хранения в БД удобно использовать
value, а в коде работать с Enum:stored_value = 2
status = OrderStatus(stored_value) # восстановили из числа
Если значение некорректно, будет
ValueError, а не тихая ошибка.---
### Автоматические значения с
auto()Когда вам не важны конкретные числа:
from enum import Enum, auto
class UserRole(Enum):
ADMIN = auto()
MANAGER = auto()
USER = auto()
Значения будут 1, 2, 3 — но вам все равно, вы работаете только с именами.
---
### Enum как замена строковым константам
Строки тоже легко перепутать:
role = "admni" # опечатка, и код падает в неожиданных местах
Вместо этого:
class Role(Enum):
ADMIN = "admin"
USER = "user"
def has_access(role: Role) -> bool:
return role is Role.ADMIN
Теперь неправильное значение просто так не проскочит.
---
### Немного безопасности и порядка
Enum — это:
- централизованный список допустимых значений;
- самодокументирующийся код;
- меньше скрытых багов из-за опечаток и “магических чисел”.
Если вы пишете игру с типами юнитов, веб‑приложение со статусами заказов или API с ролями,
enum — простой способ сделать код понятнее и надежнее уже сегодня.👍4
Разбор стандартного модуля
Модуль
---
##
Обычный список в Python плохо подходит для частых операций в начале:
---
##
Обычный словарь бросает
Другой частый сценарий — группировка:
---
##
Отлично подходит для анализа текста:
---
Итог:
-
-
-
Все три входят в стандартную библиотеку, не требуют установки и моментально делают код чище и эффективнее. Попробуйте в своих маленьких скриптах — разницу почувствуете сразу.
collections: deque, defaultdict и CounterМодуль
collections — это такой «апгрейд» стандартных структур данных в Python. Те же списки и словари, но с турбонаддувом. Разберём три самых полезных инструмента: deque, defaultdict и Counter.---
##
deque: очередь без мученийОбычный список в Python плохо подходит для частых операций в начале:
pop(0) и insert(0, x) работают медленно. deque (double-ended queue) решает это.from collections import deque
queue = deque()
queue.append('task_1')
queue.append('task_2')
queue.appendleft('urgent_task')
print(queue) # deque(['urgent_task', 'task_1', 'task_2'])
task = queue.popleft() # достаем слева O(1)
print(task) # urgent_task
deque идеально подходит для очередей, буферов, реализации алгоритмов обхода графов (BFS) и «скользящих окон».---
##
defaultdict: словарь, который не ругаетсяОбычный словарь бросает
KeyError, если ключа нет. defaultdict вместо этого создаёт значение «по умолчанию».from collections import defaultdict
word_count = defaultdict(int)
text = "python is fun and python is powerful".split()
for word in text:
word_count[word] += 1
print(word_count['python']) # 2
print(word_count['missing']) # 0, а не KeyError
Другой частый сценарий — группировка:
from collections import defaultdict
groups = defaultdict(list)
pairs = [('a', 1), ('b', 2), ('a', 3)]
for key, value in pairs:
groups[key].append(value)
print(groups) # {'a': [1, 3], 'b': [2]}
---
##
Counter: просто посчитай этоCounter — специализированный словарь для подсчёта объектов.from collections import Counter
nums = [1, 2, 2, 3, 3, 3]
c = Counter(nums)
print(c) # Counter({3: 3, 2: 2, 1: 1})
print(c[2]) # 2
print(c.most_common(1))# [(3, 3)]
Отлично подходит для анализа текста:
from collections import Counter
text = "banana bandana"
c = Counter(text.replace(" ", ""))
print(c) # Counter({'a': 6, 'n': 3, 'b': 1, 'd': 1})
print(c.most_common(2))# самые частые буквы
---
Итог:
-
deque — быстрая очередь с двух концов. -
defaultdict — словарь с автоматическими значениями по умолчанию. -
Counter — быстрый подсчёт повторяющихся элементов.Все три входят в стандартную библиотеку, не требуют установки и моментально делают код чище и эффективнее. Попробуйте в своих маленьких скриптах — разницу почувствуете сразу.
👍4
### Создание простого REST API с Falcon: минимализм в деле
Если Flask — это швейцарский нож, то Falcon — это скальпель. Минимум магии, максимум скорости и простоты. Он отлично подходит, когда нужно сделать легкий REST API без нагромождений.
---
## Установка
Falcon не входит в стандартную библиотеку, ставим:
---
## Базовый пример: «Hello, API»
Создадим минимальный REST API, который отвечает JSON-ом.
Сохрани файл как
Переходим по
---
## Обработка методов: GET и POST
Добавим ресурс для работы со списком задач. Хранить будем в памяти, как простой пример.
Теперь:
-
-
---
## Обработка одного ресурса по ID
Добавим получение одной задачи:
---
Falcon хорош тем, что не навязывает структуру проекта: вы просто пишете классы-ресурсы и явно связываете их с маршрутами. Минимум магии — максимум контроля. Для небольшого, понятного REST API этого часто более чем достаточно.
Если Flask — это швейцарский нож, то Falcon — это скальпель. Минимум магии, максимум скорости и простоты. Он отлично подходит, когда нужно сделать легкий REST API без нагромождений.
---
## Установка
Falcon не входит в стандартную библиотеку, ставим:
pip install falcon
---
## Базовый пример: «Hello, API»
Создадим минимальный REST API, который отвечает JSON-ом.
import json
import falcon
class HelloResource:
def on_get(self, req, resp):
resp.status = falcon.HTTP_200
resp.media = {"message": "Hello, Falcon API!"}
app = falcon.App()
app.add_route("/hello", HelloResource())
Сохрани файл как
app.py, а запускать удобнее через uWSGI, gunicorn или waitress. Для разработки можно использовать falcon.App вместе с wsgiref:from wsgiref.simple_server import make_server
if __name__ == "__main__":
with make_server("127.0.0.1", 8000, app) as httpd:
httpd.serve_forever()
Переходим по
http://127.0.0.1:8000/hello и получаем JSON-ответ.---
## Обработка методов: GET и POST
Добавим ресурс для работы со списком задач. Хранить будем в памяти, как простой пример.
import falcon
tasks = []
class TasksResource:
def on_get(self, req, resp):
resp.media = {"tasks": tasks}
def on_post(self, req, resp):
data = req.media # Falcon сам распарсит JSON
title = data.get("title")
if not title:
resp.status = falcon.HTTP_400
resp.media = {"error": "title is required"}
return
task = {"id": len(tasks) + 1, "title": title}
tasks.append(task)
resp.status = falcon.HTTP_201
resp.media = task
app = falcon.App()
app.add_route("/tasks", TasksResource())
Теперь:
-
GET /tasks возвращает список задач,-
POST /tasks с JSON {"title": "Learn Falcon"} добавляет новую.---
## Обработка одного ресурса по ID
Добавим получение одной задачи:
class TaskItemResource:
def on_get(self, req, resp, task_id):
task_id = int(task_id)
for task in tasks:
if task["id"] == task_id:
resp.media = task
return
resp.status = falcon.HTTP_404
resp.media = {"error": "task not found"}
app = falcon.App()
app.add_route("/tasks", TasksResource())
app.add_route("/tasks/{task_id}", TaskItemResource())
---
Falcon хорош тем, что не навязывает структуру проекта: вы просто пишете классы-ресурсы и явно связываете их с маршрутами. Минимум магии — максимум контроля. Для небольшого, понятного REST API этого часто более чем достаточно.
👍5
Как использовать
---
## Базовое использование
Результат:
Важно:
---
## Создание словаря из двух списков
Классический приём:
Результат:
---
## Unpacking: оператор
Оператор
### Транспонирование “таблицы”
Представим матрицу как список строк:
Результат:
Мы как бы “повернули” таблицу: строки стали столбцами. Это один из самых элегантных трюков с
---
## Распаковка результата
Так можно “разобрать” список пар обратно на два отдельных набора данных.
---
## С
Иногда нужно и индекс, и значения из нескольких коллекций:
---
zip и unpacking в Python: собираем пазл из данныхzip — это маленькая функция, которая решает кучу рутинных задач: объединяет списки, разворачивает таблицы, помогает красиво перебирать данные. А в паре с распаковкой (unpacking, оператор *) она становится особенно мощной.---
## Базовое использование
zipzip “склеивает” несколько итерируемых объектов по позициям:names = ["Alice", "Bob", "Charlie"]
scores = [95, 82, 78]
for name, score in zip(names, scores):
print(name, "=>", score)
Результат:
Alice => 95
Bob => 82
Charlie => 78
Важно:
zip обрезает результат по самому короткому списку.---
## Создание словаря из двух списков
Классический приём:
keys = ["host", "port", "debug"]
values = ["localhost", 8000, True]
config = dict(zip(keys, values))
print(config)
Результат:
{'host': 'localhost', 'port': 8000, 'debug': True}
---
## Unpacking: оператор
* в действииОператор
* “распаковывает” итерируемые объекты. В связке с zip это особенно полезно.### Транспонирование “таблицы”
Представим матрицу как список строк:
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
transposed = list(zip(*matrix))
print(transposed)
Результат:
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Мы как бы “повернули” таблицу: строки стали столбцами. Это один из самых элегантных трюков с
zip.---
## Распаковка результата
zipzip возвращает итератор, его тоже можно распаковать:pairs = list(zip(names, scores))
print(pairs) # [('Alice', 95), ('Bob', 82), ('Charlie', 78)]
unzipped_names, unzipped_scores = zip(*pairs)
print(unzipped_names) # ('Alice', 'Bob', 'Charlie')
print(unzipped_scores) # (95, 82, 78)
Так можно “разобрать” список пар обратно на два отдельных набора данных.
---
## С
enumerate и zip: удобный переборИногда нужно и индекс, и значения из нескольких коллекций:
for idx, (name, score) in enumerate(zip(names, scores), start=1):
print(f"{idx}. {name}: {score}")
---
zip и unpacking — это инструменты, которые делают код короче, читабельнее и “питоничнее”. Освоив их, вы начнёте мыслить данными как блоками, которые легко собирать и разбирать, словно конструктор.👍3🔥2
Python для начинающих: работа с очередями через модуль
Если вам когда‑нибудь приходилось организовывать задачи «по очереди», то модуль
### Зачем нужна очередь?
Очередь (FIFO — first in, first out) — структура, которая забирает элементы в том же порядке, в котором они были добавлены. Это удобно для:
- обработки задач по мере поступления;
- организации «конвейеров» (один поток кладёт, другой забирает);
- ограничения количества элементов (защита от переполнения памяти).
### Базовый пример:
Методы:
-
-
-
### Очередь в многопоточности
Здесь:
- один поток «производит» задачи;
- второй их «потребляет»;
-
### LIFO и приоритеты
Модуль
Стек (LIFO):
Очередь с приоритетом:
Элемент с наименьшим приоритетом (числом) будет обработан первым.
---
Модуль
queueЕсли вам когда‑нибудь приходилось организовывать задачи «по очереди», то модуль
queue — именно то, что нужно. Это стандартный модуль Python для безопасной работы с очередями в многопоточных программах. Но им удобно пользоваться и в обычных скриптах.### Зачем нужна очередь?
Очередь (FIFO — first in, first out) — структура, которая забирает элементы в том же порядке, в котором они были добавлены. Это удобно для:
- обработки задач по мере поступления;
- организации «конвейеров» (один поток кладёт, другой забирает);
- ограничения количества элементов (защита от переполнения памяти).
### Базовый пример:
Queuefrom queue import Queue
task_queue = Queue(maxsize=3) # ограничим размер
task_queue.put("task_1")
task_queue.put("task_2")
task_queue.put("task_3")
print(task_queue.full()) # True
item = task_queue.get()
print("Got:", item)
print("Empty:", task_queue.empty())
Методы:
-
put(item) — положить элемент (по умолчанию будет ждать, если очередь заполнена);-
get() — забрать элемент (будет ждать, если очередь пуста);-
full(), empty(), qsize() — состояние очереди.### Очередь в многопоточности
queue.Queue уже потокобезопасна: можно спокойно использовать ее в разных потоках без дополнительных блокировок.from queue import Queue
from threading import Thread
import time
task_queue = Queue()
def producer():
for i in range(5):
task = f"task_{i}"
print("Produce:", task)
task_queue.put(task)
time.sleep(0.2)
task_queue.put(None) # сигнал завершения
def consumer():
while True:
task = task_queue.get()
if task is None:
break
print("Consume:", task)
task_queue.task_done()
t1 = Thread(target=producer)
t2 = Thread(target=consumer)
t1.start()
t2.start()
t1.join()
t2.join()
Здесь:
- один поток «производит» задачи;
- второй их «потребляет»;
-
None — маркер завершения работы.### LIFO и приоритеты
Модуль
queue умеет не только обычные очереди.Стек (LIFO):
from queue import LifoQueue
stack = LifoQueue()
stack.put("first")
stack.put("second")
print(stack.get()) # second
print(stack.get()) # first
Очередь с приоритетом:
from queue import PriorityQueue
pq = PriorityQueue()
pq.put((2, "low"))
pq.put((1, "high"))
pq.put((3, "very_low"))
while not pq.empty():
priority, item = pq.get()
print(priority, item)
Элемент с наименьшим приоритетом (числом) будет обработан первым.
---
Модуль
queue — это простой способ навести порядок в задачах и безопасно разделить работу между потоками. Даже если вы пока не лезете в сложную многопоточность, привычка использовать очереди поможет вам строить более чистую и предсказуемую архитектуру программ.🔥4