Python для начинающих
1.24K subscribers
546 photos
3 videos
232 files
74 links
Python для начинающих
Download Telegram
Модуль time: замер и имитация задержек в коде
### Модуль 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 кажется простым, но это мощный инструмент: с ним вы можете и измерять производительность, и контролировать темп выполнения программы. А это уже шаг от “просто работает” к “работает предсказуемо и управляемо”.
👍31🔥1🤩1
Модуль enum: создание читаемых и безопасных констант
Модуль 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
Разбор стандартного модуля collections: deque, defaultdict и Counter
Разбор стандартного модуля 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
### Создание простого REST API с Falcon: минимализм в деле

Если 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
Как использовать zip и unpacking в Python
Как использовать zip и unpacking в Python: собираем пазл из данных

zip — это маленькая функция, которая решает кучу рутинных задач: объединяет списки, разворачивает таблицы, помогает красиво перебирать данные. А в паре с распаковкой (unpacking, оператор *) она становится особенно мощной.

---

## Базовое использование zip

zip “склеивает” несколько итерируемых объектов по позициям:

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.

---

## Распаковка результата zip

zip возвращает итератор, его тоже можно распаковать:

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
Работа с очередями с использованием модуля queue
Python для начинающих: работа с очередями через модуль queue

Если вам когда‑нибудь приходилось организовывать задачи «по очереди», то модуль queue — именно то, что нужно. Это стандартный модуль Python для безопасной работы с очередями в многопоточных программах. Но им удобно пользоваться и в обычных скриптах.

### Зачем нужна очередь?

Очередь (FIFO — first in, first out) — структура, которая забирает элементы в том же порядке, в котором они были добавлены. Это удобно для:

- обработки задач по мере поступления;
- организации «конвейеров» (один поток кладёт, другой забирает);
- ограничения количества элементов (защита от переполнения памяти).

### Базовый пример: Queue

from 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
Изучение структур данных: стек и очередь на Python
Изучение структур данных: стек и очередь на Python

Если вы пишете код «как-нибудь в списки», рано или поздно всё превращается в хаос. Структуры данных — это способ договориться с самим собой, как именно вы пользуетесь коллекцией. Сегодня разберём две классики: стек и очередь.

---

### Стек: принцип «последним пришёл — первым ушёл» (LIFO)

Стек — как стопка тарелок: снимаем сверху, кладём тоже сверху.

В Python стек удобно реализовать обычным списком:

stack = []

# push
stack.append(1)
stack.append(2)
stack.append(3)

print(stack) # [1, 2, 3]

# pop (снимаем с вершины)
top = stack.pop()
print(top) # 3
print(stack) # [1, 2]

# просмотр вершины без удаления
peek = stack[-1]
print(peek) # 2


Где это полезно:
- отмена действий (undo),
- обход деревьев,
- проверка правильности скобок в строке.

Мини‑пример проверки скобок:

def is_brackets_balanced(expr: str) -> bool:
stack = []
pairs = {')': '(', ']': '[', '}': '{'}
for ch in expr:
if ch in '([{':
stack.append(ch)
elif ch in ')]}':
if not stack or stack.pop() != pairs[ch]:
return False
return not stack

print(is_brackets_balanced("(a[b]{c})")) # True
print(is_brackets_balanced("(a[b]{c}")) # False


---

### Очередь: «первым пришёл — первым ушёл» (FIFO)

Очередь — как линия в супермаркете: обслуживают по порядку.

Наивный вариант через список:

queue = []
queue.append("task1")
queue.append("task2")
queue.append("task3")

first = queue.pop(0) # медленно на больших данных
print(first) # task1


Проблема: pop(0) сдвигает весь список, что дорого по времени. Для нормальной очереди используем collections.deque:

from collections import deque

queue = deque()

queue.append("task1")
queue.append("task2")
queue.append("task3")

first = queue.popleft()
print(first) # task1
print(queue) # deque(['task2', 'task3'])


deque оптимизирован под быстрые операции с обоих концов.

---

### Когда что использовать

- Стек — когда важно вернуться назад: история браузера, рекурсивные алгоритмы, разбор выражений.
- Очередь — когда задачи должны обрабатываться по мере поступления: обработка запросов, планирование задач, очереди сообщений.

Понимание этих двух структур — фундамент для более сложных: деревьев, графов, очередей с приоритетами. И да, в Python почти всегда достаточно list и deque, вопрос только в том, как вы их используете.
2👍1🔥1
Сериализация с помощью модуля pickle: плюсы и минусы
Сериализация с помощью pickle: плюсы и минусы

В какой‑то момент каждый питонист приходит к вопросу: как сохранить объект «как есть», а потом просто загрузить и продолжить работу? Стандартный ответ в мире Python — модуль pickle. Он умеет превращать почти любые объекты в байты и обратно. Но у этой магии есть цена.

---

### Что такое pickle?

pickle — это бинарный формат сериализации Python‑объектов.
То есть:

- сериализация — превращаем объект в байтовую строку;
- десериализация — восстанавливаем объект из байтов.

Простейший пример:

import pickle

data = {"user": "alice", "score": 42, "tags": ["python", "pickle"]}

with open("data.pkl", "wb") as f:
pickle.dump(data, f)

with open("data.pkl", "rb") as f:
loaded = pickle.load(f)

print(loaded)


Выглядит идеально: один модуль, одна строка — и ваш словарь переезжает на диск.

---

### Сильные стороны pickle

1. Работает «из коробки» почти со всем
Списки, словари, множества, пользовательские классы, вложенные структуры — pickle справится.

2. Минимум кода и усилий
Нет нужды вручную описывать поля, типы и схемы.

3. Удобен для быстрых прототипов и кэшей
Например, можно сохранить обученную модель или результат тяжёлых вычислений:

import pickle

class Model:
def __init__(self, weights):
self.weights = weights

model = Model(weights=[0.1, 0.5, 0.9])

with open("model.pkl", "wb") as f:
pickle.dump(model, f)

# ... позже
with open("model.pkl", "rb") as f:
restored_model = pickle.load(f)


---

### Обратная сторона: минусы и подводные камни

1. Опасность безопасности
Главное правило:

> Никогда не делайте pickle.load() на данных из непроверенного источника.

pickle может при десериализации выполнять произвольный код. Это не баг, а особенность протокола.

2. Формат специфичен для Python
Другие языки pickle не понимают. Обмениваться данными с внешними сервисами удобнее через JSON, Protobuf и т.п.

3. Хрупкость к изменению кода
Измените структуру класса — старые .pkl могут перестать корректно загружаться. Для долгосрочного хранения это риск.

4. Бинарный и нечитаемый
Нельзя просто открыть файл в редакторе и «глазами» понять, что внутри.

---

### Когда использовать pickle, а когда — нет?

Подходит:

- временные кэши;
- быстрые прототипы;
- сохранение внутренних объектов между запусками скрипта;
- сценарии, где и сохраняет, и загружает один и тот же контролируемый код.

Лучше избегать:

- всё, что связано с внешними пользователями;
- публичные API и сетевые протоколы;
- долгосрочное хранение важных данных;
- ситуации, где нужен человекочитаемый формат.

---

pickle — это мощный, но «острый» инструмент. Для внутренних задач разработчика он экономит часы работы. Для обмена данными и публичных сервисов чаще всего безопаснее и разумнее выбрать более строгие и переносимые форматы.
👍2🔥2
Создание простого cli-инструмента с аргументами через argparse
Создаём свой первый CLI-инструмент с argparse

Рано или поздно каждый питонист приходит к мысли: «Хочу свою консольную утилиту!» Что‑нибудь, что запускается из терминала, принимает аргументы и делает полезное дело. В Python для этого есть отличный модуль argparse.

---

### Зачем вообще нужны аргументы?

Без аргументов ваш скрипт живёт в режиме «одна команда — один результат». А с аргументами можно:

- менять режим работы (--verbose, --quiet)
- передавать файлы (--input file.txt)
- переключать формат вывода (--json, --text)

И всё это без изменения кода — просто другими параметрами запуска.

---

### Базовый пример: мини-калькулятор

Создадим файл calc.py, который принимает два числа и операцию:

import argparse

def main():
parser = argparse.ArgumentParser(
description="Simple CLI calculator"
)
parser.add_argument("a", type=float, help="First number")
parser.add_argument("b", type=float, help="Second number")
parser.add_argument(
"-o", "--operation",
choices=["add", "sub", "mul", "div"],
default="add",
help="Math operation (default: add)"
)

args = parser.parse_args()

if args.operation == "add":
result = args.a + args.b
elif args.operation == "sub":
result = args.a - args.b
elif args.operation == "mul":
result = args.a * args.b
elif args.operation == "div":
result = args.a / args.b

print(result)

if __name__ == "__main__":
main()


Теперь можно запускать:

python calc.py 2 3
python calc.py 10 5 -o sub
python calc.py 3 4 --operation mul


argparse уже умеет:

- показывать --help с описанием
- проверять типы (type=float)
- ограничивать значения (choices=[...])
- задавать значения по умолчанию (default=)

---

### Пара фишек, которые часто забывают

1. Флаг без значения — просто наличие/отсутствие:

parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Show detailed output"
)


Теперь --verbose даёт args.verbose == True.

2. Переиспользование логики

Лучше выносить «деловую» логику в отдельные функции — тогда скрипт проще тестировать и расширять:

def calculate(a, b, operation):
if operation == "add":
return a + b
if operation == "sub":
return a - b
if operation == "mul":
return a * b
if operation == "div":
return a / b
raise ValueError("Unknown operation")


---

CLI-инструмент в Python — это буквально несколько строк с argparse, а ощущение уже как от «настоящей» утилиты. Попробуйте переписать свой следующий скрипт так, чтобы он принимал аргументы командной строки — разница в удобстве будет огромной.
👍3🔥1
Как работать с постраничной загрузкой данных из API
Python для начинающих: как подружиться с постраничной загрузкой из API

Если вы когда‑нибудь тянули данные из API, то почти наверняка сталкивались с ситуацией: «Тут 10 записей, а где остальные 10 000?». Ответ — в пагинации (postраничной загрузке). API редко отдают всё сразу, чтобы не положить ни себя, ни вас.

Разберёмся, какие бывают схемы пагинации и как с ними работать в Python.

---

### 1. Пагинация по номеру страницы

Классика: у вас есть page и per_page.

import requests

BASE_URL = "https://api.example.com/items"

def fetch_page(page: int, per_page: int = 50):
params = {"page": page, "per_page": per_page}
response = requests.get(BASE_URL, params=params, timeout=10)
response.raise_for_status()
return response.json()

def fetch_all_items():
page = 1
all_items = []
while True:
data = fetch_page(page)
items = data.get("items", [])
if not items:
break
all_items.extend(items)
if not data.get("has_next"): # или проверяем page >= total_pages
break
page += 1
return all_items


Ключевой момент — условие выхода. Часто API возвращает has_next, total_pages или next_page.

---

### 2. Пагинация по смещению (offset/limit)

Тут вам дают offset (сдвиг) и limit (размер пачки):

def fetch_batch(offset: int, limit: int = 100):
params = {"offset": offset, "limit": limit}
response = requests.get(BASE_URL, params=params, timeout=10)
response.raise_for_status()
return response.json()

def fetch_all_items_offset():
offset = 0
limit = 100
all_items = []
while True:
data = fetch_batch(offset, limit)
items = data.get("items", [])
if not items:
break
all_items.extend(items)
offset += limit
return all_items


Минус offset-подхода — при больших объёмах данных он может быть медленнее.

---

### 3. Пагинация по курсору (cursor / next)

Самый «современный» вариант: API возвращает ссылку или токен для следующей страницы.

def fetch_all_items_cursor():
url = BASE_URL
all_items = []
while url:
response = requests.get(url, timeout=10)
response.raise_for_status()
data = response.json()
items = data.get("items", [])
all_items.extend(items)
url = data.get("next") # None, если страниц больше нет
return all_items


Здесь нет ни страниц, ни offset — только next. Это устойчиво к изменениям данных на сервере.

---

### Полезные рекомендации

- Всегда ставьте timeout в requests.get.
- Уважайте лимиты API: иногда нужно делать time.sleep() между запросами.
- Логируйте прогресс: len(all_items) поможет понять, что вы действительно двигаетесь.

Понимая три паттерна пагинации — page, offset, cursor — вы сможете уверенно забирать из API тысячи и миллионы записей, не утонув ни в ошибках, ни в лимитах.
👍3
Работа с форматом YAML в Python с использованием PyYAML