Работа с мультипроцессорностью в Python: модуль
Если ваш скрипт считает что‑то «тяжёлое» и при этом использует только одно ядро процессора — он просто ленится. Модуль
---
### Почему не
В Python есть GIL — глобальная блокировка интерпретатора. Потоки (
---
### Простой пример: распараллеливаем вычисления
Допустим, у нас есть тяжёлая функция:
Ключевые моменты:
-
-
-
Каждый процесс считает свою версию
---
### Используем пул процессов:
Чаще всего нужно применить одну функцию к множеству входных данных. Для этого идеален
Что важно:
-
-
- Функция, передаваемая в процессы, должна быть определена на верхнем уровне модуля (не внутри другой функции, не lambda).
---
### Передача данных между процессами
У процессов своя память, поэтому обычные переменные не разделяются. Можно использовать:
-
-
Пример с
---
multiprocessingЕсли ваш скрипт считает что‑то «тяжёлое» и при этом использует только одно ядро процессора — он просто ленится. Модуль
multiprocessing позволяет загрузить все ядра и реально ускорить выполнение CPU‑интенсивных задач.---
### Почему не
threading?В Python есть GIL — глобальная блокировка интерпретатора. Потоки (
threading) отлично подходят для I/O (сетевые запросы, файлы), но почти не ускоряют чистые вычисления: байткод всё равно исполняется в один поток.multiprocessing запускает несколько процессов, у каждого свой интерпретатор и свой GIL. Значит, они могут работать параллельно на разных ядрах.---
### Простой пример: распараллеливаем вычисления
Допустим, у нас есть тяжёлая функция:
from multiprocessing import Process
import time
def heavy_job(n):
s = 0
for i in range(10_000_000):
s += (i * n) % 7
print(f"Result for {n}: {s}")
if __name__ == "__main__":
t0 = time.time()
processes = []
for num in [1, 2, 3, 4]:
p = Process(target=heavy_job, args=(num,))
p.start()
processes.append(p)
for p in processes:
p.join()
print("Time:", time.time() - t0)
Ключевые моменты:
-
if __name__ == "__main__": обязателен на Windows и macOS, иначе процессы начнут бесконечно плодиться. -
Process(target=..., args=...) — создаём процесс и указываем функцию. -
start() — запускаем, join() — ждём завершения. Каждый процесс считает свою версию
heavy_job, ядра загружаются параллельно.---
### Используем пул процессов:
PoolЧаще всего нужно применить одну функцию к множеству входных данных. Для этого идеален
Pool.from multiprocessing import Pool
import time
def square(x):
return x * x
if __name__ == "__main__":
data = list(range(10))
t0 = time.time()
with Pool(processes=4) as pool:
result = pool.map(square, data)
print("Result:", result)
print("Time:", time.time() - t0)
Что важно:
-
Pool сам управляет созданием и завершением процессов.-
map работает примерно как встроенная map, только параллельно.- Функция, передаваемая в процессы, должна быть определена на верхнем уровне модуля (не внутри другой функции, не lambda).
---
### Передача данных между процессами
У процессов своя память, поэтому обычные переменные не разделяются. Можно использовать:
-
Queue — безопасная очередь для обмена сообщениями.-
Manager — создаёт объекты, которые можно разделять (списки, словари).Пример с
Queue:from multiprocessing import Process, Queue
def worker(q, x):
q.put(x * 2)
if __name__ == "__main__":
q = Queue()
processes = []
for i in range(5):
p = Process(target=worker, args=(q, i))
p.start()
processes.append(p)
for p in processes:
p.join()
results = [q.get() for _ in range(5)]
print(results)
---
multiprocessing — это способ превратить ваш скрипт в мини‑кластер на одном компьютере. Если задача упирается в процессор, а не в диск или сеть, имеет смысл попробовать распараллеливание: часто выигрыш по времени оказывается впечатляющим.👍3
Создаем локального чат-бота на Python с помощью
Локальный чат-бот — это отличный способ понять, как устроены сетевые приложения: клиент, сервер, обмен сообщениями. Без веб-фреймворков и магии — только
---
### Идея
Мы сделаем простой консольный чат-бот:
- Сервер: ждет подключений, принимает сообщения и отвечает.
- Клиент: подключается к боту и общается с ним в одном терминале.
Работать будем только на
---
### Шаг 1. Простейший сервер-бот
Сервер слушает порт, принимает соединение и в цикле отвечает пользователю.
---
### Шаг 2. Клиент для общения с ботом
---
### Что здесь важно понять
1.
2.
3.
4. Локальный бот можно усложнять:
- добавлять обработку команд,
- сохранять историю диалога,
- подключать простейший ИИ или правила.
Такой минималистичный проект отлично прокачивает понимание сетей и одновременно дает живой, работающий результат — своего собственного чат-бота, полностью под вашим контролем.
socketЛокальный чат-бот — это отличный способ понять, как устроены сетевые приложения: клиент, сервер, обмен сообщениями. Без веб-фреймворков и магии — только
socket и немного логики.---
### Идея
Мы сделаем простой консольный чат-бот:
- Сервер: ждет подключений, принимает сообщения и отвечает.
- Клиент: подключается к боту и общается с ним в одном терминале.
Работать будем только на
localhost, без выхода в интернет.---
### Шаг 1. Простейший сервер-бот
Сервер слушает порт, принимает соединение и в цикле отвечает пользователю.
# server.py
import socket
HOST = "127.0.0.1"
PORT = 5000
def generate_reply(message: str) -> str:
message = message.lower()
if "hello" in message:
return "Hi! I'm your local socket bot."
if "help" in message:
return "Try: hello, time, bye."
if "time" in message:
from datetime import datetime
return f"Current time: {datetime.now().strftime('%H:%M:%S')}"
if "bye" in message:
return "Goodbye! Closing connection."
return "I don't understand. Type 'help'."
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen(1)
print(f"Bot is listening on {HOST}:{PORT}...")
conn, addr = s.accept()
with conn:
print(f"Connected by {addr}")
while True:
data = conn.recv(1024)
if not data:
break
user_msg = data.decode("utf-8")
reply = generate_reply(user_msg)
conn.sendall(reply.encode("utf-8"))
if "bye" in user_msg.lower():
break
---
### Шаг 2. Клиент для общения с ботом
# client.py
import socket
HOST = "127.0.0.1"
PORT = 5000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
print("Connected to local bot. Type messages, 'bye' to exit.")
while True:
user_msg = input("> ")
s.sendall(user_msg.encode("utf-8"))
data = s.recv(1024)
bot_reply = data.decode("utf-8")
print("Bot:", bot_reply)
if "bye" in user_msg.lower():
break
---
### Что здесь важно понять
1.
socket.AF_INET, socket.SOCK_STREAM — обычный TCP-сокет.2.
bind + listen + accept — базовый цикл сервера.3.
sendall и recv — протокол обмена байтами; мы сами решаем, во что их превращать (здесь — строки UTF-8).4. Локальный бот можно усложнять:
- добавлять обработку команд,
- сохранять историю диалога,
- подключать простейший ИИ или правила.
Такой минималистичный проект отлично прокачивает понимание сетей и одновременно дает живой, работающий результат — своего собственного чат-бота, полностью под вашим контролем.
👍4❤1
Как укротить дату в Python: форматирование с
У datetime в Python есть суперспособность — превращать «сырую» дату в аккуратную строку нужного формата. Делает он это с помощью метода
Начнем с базы:
### Самые полезные коды формата
Вот минимальный «набор выживания»:
-
-
-
-
-
-
-
Комбинируя их, можно получить любой стиль:
### День недели и месяц словами
Если нужно что-то «человечнее», вроде «Thu, April 09, 2026»:
-
-
-
-
### Формат для логов и файлов
Частая задача — сделать «безопасное» имя файла с датой внутри (никаких пробелов и двоеточий):
### Маленький лайфхак
Если увидели где-то странную строку вида
strftimeУ datetime в Python есть суперспособность — превращать «сырую» дату в аккуратную строку нужного формата. Делает он это с помощью метода
strftime. Запомните: string format time.Начнем с базы:
from datetime import datetime
now = datetime.now()
formatted = now.strftime("%Y-%m-%d %H:%M:%S")
print(formatted) # например: 2026-04-09 14:37:12
strftime принимает строку-шаблон, где специальные %-коды заменяются на кусочки даты и времени.### Самые полезные коды формата
Вот минимальный «набор выживания»:
-
%Y — год полностью, 2026-
%y — год из двух цифр, 26-
%m — месяц, 01–12-
%d — день месяца, 01–31-
%H — часы (24-часовой формат), 00–23-
%M — минуты, 00–59-
%S — секунды, 00–59Комбинируя их, можно получить любой стиль:
from datetime import datetime
now = datetime.now()
iso_style = now.strftime("%Y-%m-%d")
europe_style = now.strftime("%d.%m.%Y")
us_style = now.strftime("%m/%d/%Y")
time_only = now.strftime("%H:%M")
print(iso_style)
print(europe_style)
print(us_style)
print(time_only)
### День недели и месяц словами
Если нужно что-то «человечнее», вроде «Thu, April 09, 2026»:
-
%a — краткое название дня недели (Mon)-
%A — полное название дня недели (Monday)-
%b — краткое название месяца (Apr)-
%B — полное название месяца (April)from datetime import datetime
now = datetime.now()
pretty = now.strftime("%A, %d %B %Y, %H:%M")
log_style = now.strftime("[%Y-%m-%d %H:%M:%S]")
print(pretty)
print(log_style)
### Формат для логов и файлов
Частая задача — сделать «безопасное» имя файла с датой внутри (никаких пробелов и двоеточий):
from datetime import datetime
now = datetime.now()
filename = now.strftime("backup_%Y%m%d_%H%M%S.zip")
print(filename) # например: backup_20260409_143712.zip
### Маленький лайфхак
Если увидели где-то странную строку вида
2026-04-09T14:37:12 — это почти ISO 8601. Такое легко получить:from datetime import datetime
now = datetime.now()
iso_like = now.strftime("%Y-%m-%dT%H:%M:%S")
print(iso_like)
strftime — это ваш личный дизайнер дат. Один и тот же объект datetime, но десятки разных представлений — под логи, интерфейсы, имена файлов и отчеты. Главное — выучить несколько кодов, а остальное легко комбинировать как конструктор.👍5🔥2
Python для начинающих: как
У каждого начинающего питониста в какой‑то момент в коде появляются «магические» значения:
Через пару недель вы сами не вспомните, что такое
---
## Что такое Enum?
Теперь:
Сразу видно, какие вообще существуют статусы и какие из них допустимы.
---
## Почему это лучше, чем строки и числа?
1. Меньше опечаток
При опечатке Python сразу ругнется:
2. Гарантия допустимых значений
С
3. Самодокументируемость
Вместо
---
## Числовые Enum: пример с правами доступа
Иногда удобно использовать числа — например, для уровней доступа:
---
## Enum и словари
Частый паттерн — использовать
Так вы не перепутаете
---
## Вывод
- заменяет «магические» числа и строки;
- делает список допустимых значений явным;
- уменьшает количество скрытых ошибок и опечаток;
- улучшает читаемость без дополнительных комментариев.
Как только у вас появляются повторяющиеся значения с ограниченным набором вариантов — статусы, роли, режимы, окружения — это сигнал: здесь пора использовать
Enum делает код понятнееУ каждого начинающего питониста в какой‑то момент в коде появляются «магические» значения:
status = "ok" # что значит?
role = 1 # а это?
Через пару недель вы сами не вспомните, что такое
1 и чем "ok" отличается от "ready". Для таких случаев в Python есть модуль enum, который превращает разрозненные значения в понятные, самодокументирующиеся константы.---
## Что такое Enum?
Enum (перечисление) — это набор именованных значений. Вместо голых чисел и строк вы используете осмысленные имена:from enum import Enum
class OrderStatus(Enum):
CREATED = "created"
PAID = "paid"
SHIPPED = "shipped"
CANCELED = "canceled"
Теперь:
status = OrderStatus.PAID
if status is OrderStatus.PAID:
print("Send receipt")
Сразу видно, какие вообще существуют статусы и какие из них допустимы.
---
## Почему это лучше, чем строки и числа?
1. Меньше опечаток
# Плохо
if status == "padi": # незаметная ошибка
...
# Хорошо
if status is OrderStatus.PAID:
...
При опечатке Python сразу ругнется:
OrderStatus.PADI просто не существует.2. Гарантия допустимых значений
def change_status(status: OrderStatus):
print("New status:", status)
change_status(OrderStatus.SHIPPED) # ок
change_status("lost") # логическая ошибка
С
Enum становится очевидно, что "lost" — лишний статус.3. Самодокументируемость
Вместо
status = 3 — status = OrderStatus.CANCELED. Смысл читается без комментариев.---
## Числовые Enum: пример с правами доступа
Иногда удобно использовать числа — например, для уровней доступа:
from enum import IntEnum, auto
class AccessLevel(IntEnum):
GUEST = 1
USER = 2
ADMIN = 3
def can_delete_posts(level: AccessLevel) -> bool:
return level >= AccessLevel.ADMIN
print(can_delete_posts(AccessLevel.USER)) # False
print(can_delete_posts(AccessLevel.ADMIN)) # True
IntEnum ведет себя как int, можно сравнивать уровни, сортировать и т.д.---
## Enum и словари
Частый паттерн — использовать
Enum как ключи конфигураций:from enum import Enum
class Env(Enum):
DEV = "dev"
STAGE = "stage"
PROD = "prod"
config = {
Env.DEV: {"debug": True, "db": "sqlite:///:memory:"},
Env.PROD: {"debug": False, "db": "postgres://prod-db"},
}
current_env = Env.DEV
db_url = config[current_env]["db"]
print(db_url)
Так вы не перепутаете
"dev" и "prod" и сразу видите все возможные окружения.---
## Вывод
Enum — это простой способ навести порядок в коде:- заменяет «магические» числа и строки;
- делает список допустимых значений явным;
- уменьшает количество скрытых ошибок и опечаток;
- улучшает читаемость без дополнительных комментариев.
Как только у вас появляются повторяющиеся значения с ограниченным набором вариантов — статусы, роли, режимы, окружения — это сигнал: здесь пора использовать
Enum.👍4
### Python для начинающих: доступ по паролю с помощью shelve
Представьте: вы пишете небольшой консольный “личный дневник” или мини‑админку для заметок. Хранить данные в чистом виде в файле не хочется, база данных — слишком тяжело. А ещё нужен простой механизм ограничения доступа по логину и паролю.
Для таких задач отлично подходит модуль
---
## Что такое shelve
- ключи — строки;
- значения — любые сериализуемые объекты (списки, словари, классы и т.п.);
- данные автоматически сохраняются на диск.
Минимальный пример:
---
## Простейшая система доступа с сессиями
Сессия — это запись о том, что пользователь успешно вошёл, плюс немного данных о нём.
### 1. Регистрация пользователя
### 2. Логин и создание сессии
---
## Проверка доступа по сессии
Добавим проверку, что у пользователя есть действительная сессия:
Теперь можно ограничивать доступ к действиям:
---
## Когда shelve — удачный выбор
- небольшие консольные утилиты;
- учебные проекты;
- прототипы, где не хочется поднимать СУБД.
Важно:
Представьте: вы пишете небольшой консольный “личный дневник” или мини‑админку для заметок. Хранить данные в чистом виде в файле не хочется, база данных — слишком тяжело. А ещё нужен простой механизм ограничения доступа по логину и паролю.
Для таких задач отлично подходит модуль
shelve — встроенное “полу‑хранилище, полу‑словарь” на диске.---
## Что такое shelve
shelve позволяет хранить произвольные Python‑объекты в файле, обращаясь к ним как к словарю:- ключи — строки;
- значения — любые сериализуемые объекты (списки, словари, классы и т.п.);
- данные автоматически сохраняются на диск.
Минимальный пример:
import shelve
with shelve.open("session_db") as db:
db["user1"] = {"name": "Alice", "role": "admin"}
with shelve.open("session_db") as db:
print(db["user1"])
---
## Простейшая система доступа с сессиями
Сессия — это запись о том, что пользователь успешно вошёл, плюс немного данных о нём.
### 1. Регистрация пользователя
import shelve
import hashlib
def hash_password(password: str) -> str:
return hashlib.sha256(password.encode("utf-8")).hexdigest()
def register_user(username: str, password: str) -> None:
with shelve.open("users_db") as users:
if username in users:
raise ValueError("User already exists")
users[username] = {
"password_hash": hash_password(password),
"role": "user",
}
### 2. Логин и создание сессии
import os
import time
def create_session(username: str) -> str:
session_id = os.urandom(16).hex()
with shelve.open("sessions_db") as sessions:
sessions[session_id] = {
"username": username,
"created_at": time.time(),
}
return session_id
def login(username: str, password: str) -> str | None:
with shelve.open("users_db") as users:
user = users.get(username)
if not user:
return None
if user["password_hash"] != hash_password(password):
return None
return create_session(username)
session_id можно, например, хранить в файле, окружении, передавать параметром и т.п.---
## Проверка доступа по сессии
Добавим проверку, что у пользователя есть действительная сессия:
SESSION_TTL = 3600 # 1 hour
def get_current_user(session_id: str) -> dict | None:
with shelve.open("sessions_db", writeback=True) as sessions:
session = sessions.get(session_id)
if not session:
return None
is_expired = time.time() - session["created_at"] > SESSION_TTL
if is_expired:
del sessions[session_id]
return None
with shelve.open("users_db") as users:
return users.get(session["username"])
Теперь можно ограничивать доступ к действиям:
def protected_action(session_id: str) -> None:
user = get_current_user(session_id)
if not user:
print("Access denied")
return
print(f"Welcome, {user['role']}!")
---
## Когда shelve — удачный выбор
- небольшие консольные утилиты;
- учебные проекты;
- прототипы, где не хочется поднимать СУБД.
Важно:
shelve не рассчитан на высокую нагрузку и многопоточность, но для “домашних” Python‑скриптов это простой и удобный способ реализовать хранение пользователей и сессий без лишних зависимостей.🔥3
Применение regular expressions для валидации email адреса
Валидация email — классическая задача, с которой рано или поздно сталкивается почти каждый начинающий питонист. Можно проверять строку “на глаз”, разбирать её по
### Почему именно regular expressions?
Регулярки позволяют описать формат email одной строкой-правилом:
- есть одна и только одна
- до
- после
- в конце — доменная зона: минимум 2 буквы (
### Базовый пример
Здесь
### Валидация списка и фильтрация
Представим, что у нас есть “сырые” данные, и нужно оставить только корректные адреса:
Комбинация
### На что обратить внимание
1. Идеальной регулярки для email не существует. Полный стандарт RFC очень сложный; в продакшене нередко используют библиотечные валидаторы или отправку тестового письма.
2. Регулярка — предварительный фильтр. Она отсекает заведомо неверные строки, но не гарантирует, что почтовый ящик существует.
3. Не переусложняйте. Для большинства веб-форм достаточно аккуратного, но не “RFC-идеального” шаблона — вроде того, что выше.
Регулярные выражения в Python — это инструмент, который позволяет превратить разрозненные проверки в одно чёткое правило. Освоив их на простом примере с email, дальше проще разбирать и более сложные шаблоны.
Валидация email — классическая задача, с которой рано или поздно сталкивается почти каждый начинающий питонист. Можно проверять строку “на глаз”, разбирать её по
@ и точкам, но гораздо мощнее и гибче использовать регулярные выражения.### Почему именно regular expressions?
Регулярки позволяют описать формат email одной строкой-правилом:
- есть одна и только одна
@- до
@ — допустимые символы (буквы, цифры, _ . + -)- после
@ — домен: буквы, цифры, дефис, точки- в конце — доменная зона: минимум 2 буквы (
.com, .ru, .info)### Базовый пример
import re
EMAIL_PATTERN = re.compile(
r"^[a-zA-Z0-9_.+-]+" # локальная часть
r"@" # символ @
r"[a-zA-Z0-9-]+" # домен
r"(\.[a-zA-Z0-9-]+)*" # поддомены
r"\.[a-zA-Z]{2,}$" # доменная зона
)
def is_valid_email(email: str) -> bool:
return EMAIL_PATTERN.match(email) is not None
test_emails = [
"user@example.com",
"user.name+tag@gmail.com",
"bad@@example.com",
"no-domain@",
"admin@mail-server.co.uk",
]
for addr in test_emails:
print(addr, "=>", is_valid_email(addr))
Здесь
EMAIL_PATTERN компилируется один раз, а потом много раз переиспользуется — так быстрее, чем вызывать re.match со строкой-шаблоном каждый раз.### Валидация списка и фильтрация
Представим, что у нас есть “сырые” данные, и нужно оставить только корректные адреса:
def filter_valid_emails(emails: list[str]) -> list[str]:
return [e for e in emails if is_valid_email(e)]
raw_emails = [
" boss@company.com ",
"invalid@domain",
"user@sub.domain.org",
"no-at-sign.com",
]
clean_emails = [e.strip() for e in raw_emails]
valid_emails = filter_valid_emails(clean_emails)
print(valid_emails)
Комбинация
strip() + регулярка даёт простой, но рабочий пайплайн очистки.### На что обратить внимание
1. Идеальной регулярки для email не существует. Полный стандарт RFC очень сложный; в продакшене нередко используют библиотечные валидаторы или отправку тестового письма.
2. Регулярка — предварительный фильтр. Она отсекает заведомо неверные строки, но не гарантирует, что почтовый ящик существует.
3. Не переусложняйте. Для большинства веб-форм достаточно аккуратного, но не “RFC-идеального” шаблона — вроде того, что выше.
Регулярные выражения в Python — это инструмент, который позволяет превратить разрозненные проверки в одно чёткое правило. Освоив их на простом примере с email, дальше проще разбирать и более сложные шаблоны.
🔥3👍1
### Как использовать
В Python многие разработчики знают про исключения, но гораздо реже используют предупреждения. А зря: модуль
Предупреждение — это сигнал: “что‑то не так, но мы еще можем продолжать”.
---
## Базовый пример: ваше первое предупреждение
Здесь вместо аварийного
---
## Свой класс предупреждений
Как и с исключениями, вы можете создавать свои типы:
Наследуемся от
---
## Управление показом предупреждений
Иногда предупреждения слишком шумные.
Теперь все
Теперь любое
---
## Локальное подавление предупреждений
Если “шумит” только конкретный участок кода:
Вне
---
## Когда использовать предупреждения, а не исключения
Предупреждения особенно полезны, когда:
- используется устаревший параметр, но вы пока его поддерживаете;
- подставляется значение по умолчанию вместо отсутствующего;
- результат “подозрительно”, но еще пригоден;
- вы готовите пользователей к будущим изменениям API.
warnings для генерации пользовательских предупрежденийВ Python многие разработчики знают про исключения, но гораздо реже используют предупреждения. А зря: модуль
warnings позволяет мягко сообщить пользователю о проблеме, не ломая выполнение программы.Предупреждение — это сигнал: “что‑то не так, но мы еще можем продолжать”.
---
## Базовый пример: ваше первое предупреждение
import warnings
def divide(a, b):
if b == 0:
warnings.warn("Division by zero replaced with 0", RuntimeWarning)
return 0
return a / b
print(divide(10, 0))
Здесь вместо аварийного
ZeroDivisionError мы возвращаем безопасное значение и генерируем предупреждение. Тип RuntimeWarning помогает понять характер проблемы.---
## Свой класс предупреждений
Как и с исключениями, вы можете создавать свои типы:
import warnings
class ConfigWarning(UserWarning):
pass
def load_config(config):
if "timeout" not in config:
warnings.warn("Missing 'timeout', using default = 30", ConfigWarning)
return {**config, "timeout": 30}
return config
cfg = load_config({"host": "example.com"})
Наследуемся от
UserWarning — это стандартный базовый класс для пользовательских предупреждений. Так их легко отфильтровать или обработать отдельно.---
## Управление показом предупреждений
Иногда предупреждения слишком шумные.
warnings позволяет управлять их поведением:import warnings
warnings.filterwarnings(
"ignore", # действие: "ignore", "default", "error", "once", ...
category=ConfigWarning # фильтрация по типу
)
Теперь все
ConfigWarning будут скрыты. А так можно превратить предупреждение в исключение — полезно для тестов:warnings.filterwarnings("error", category=ConfigWarning)
Теперь любое
ConfigWarning рухнет как ошибка.---
## Локальное подавление предупреждений
Если “шумит” только конкретный участок кода:
import warnings
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=ConfigWarning)
load_config({"host": "example.com"}) # тут предупреждений не будет
Вне
with поведение возвращается к обычному.---
## Когда использовать предупреждения, а не исключения
Предупреждения особенно полезны, когда:
- используется устаревший параметр, но вы пока его поддерживаете;
- подставляется значение по умолчанию вместо отсутствующего;
- результат “подозрительно”, но еще пригоден;
- вы готовите пользователей к будущим изменениям API.
warnings — отличный инструмент “вежливой строгости”: код работает, но пользователь заранее узнает, где его может ждать сюрприз.👍3
Создание простого API с
Когда слышишь слово «API», кажется, что сейчас понадобятся Django, Flask и гора магии. Но под капотом всё начинается с очень простой идеи: принять HTTP‑запрос и вернуть HTTP‑ответ. В стандартной библиотеке Python уже есть всё, чтобы сделать мини‑API буквально в несколько строк.
Ниже — современный вариант на Python 3 с модулем
### Мини‑сервер на
Сделаем API, у которого есть два маршрута:
-
-
Запусти этот скрипт и проверь:
-
-
или из терминала:
### А как в Python 2?
Логика та же, только модуль другой:
и без
### Что полезно заметить
-
- Метод
-
Такой «ручной» подход не заменяет Flask или FastAPI, но отлично прокачивает понимание основ и помогает быстро накидать простое внутреннее API или заглушку для тестов.
BaseHTTPServer / http.serverКогда слышишь слово «API», кажется, что сейчас понадобятся Django, Flask и гора магии. Но под капотом всё начинается с очень простой идеи: принять HTTP‑запрос и вернуть HTTP‑ответ. В стандартной библиотеке Python уже есть всё, чтобы сделать мини‑API буквально в несколько строк.
Ниже — современный вариант на Python 3 с модулем
http.server. Для Python 2 можно использовать те же идеи, просто заменить импорты.### Мини‑сервер на
http.serverСделаем API, у которого есть два маршрута:
-
GET / — вернёт простое сообщение-
GET /api/time — вернёт текущее время в JSONfrom http.server import HTTPServer, BaseHTTPRequestHandler
import json
from datetime import datetime
class SimpleAPIHandler(BaseHTTPRequestHandler):
def _set_headers(self, status=200, content_type="application/json"):
self.send_response(status)
self.send_header("Content-Type", content_type)
self.end_headers()
def do_GET(self):
if self.path == "/":
self._set_headers(content_type="text/plain; charset=utf-8")
self.wfile.write(b"Welcome to Simple API")
elif self.path == "/api/time":
self._set_headers()
data = {"time": datetime.utcnow().isoformat() + "Z"}
self.wfile.write(json.dumps(data).encode("utf-8"))
else:
self._set_headers(status=404)
data = {"error": "Not found"}
self.wfile.write(json.dumps(data).encode("utf-8"))
def run_server(host="127.0.0.1", port=8000):
server_address = (host, port)
httpd = HTTPServer(server_address, SimpleAPIHandler)
print(f"Serving on http://{host}:{port}")
httpd.serve_forever()
if __name__ == "__main__":
run_server()
Запусти этот скрипт и проверь:
-
http://127.0.0.1:8000/-
http://127.0.0.1:8000/api/timeили из терминала:
curl http://127.0.0.1:8000/api/time
### А как в Python 2?
Логика та же, только модуль другой:
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
и без
encode("utf-8"), там всё байтовое по умолчанию. Но лучше всё‑таки ориентироваться на Python 3 — Python 2 уже давно снят с поддержки.### Что полезно заметить
-
BaseHTTPRequestHandler даёт тебе полный контроль над протоколом — идеально, чтобы понять, как на самом деле работает HTTP.- Метод
do_GET вызывается для запросов GET. Аналогично можно реализовать do_POST, do_PUT, do_DELETE.-
self.path — это путь из URL, на нём легко построить свою мини‑маршрутизацию без фреймворков.Такой «ручной» подход не заменяет Flask или FastAPI, но отлично прокачивает понимание основ и помогает быстро накидать простое внутреннее API или заглушку для тестов.
🔥3❤1👍1
Python для начинающих: приручаем ошибки ввода с помощью
Любая программа, в которую что‑то вводит человек, рано или поздно ломается. Пользователь вместо числа вводит
В Python для этого есть связка
«Попробуй выполнить этот код. Если случится ошибка — поймай её и сделай что‑нибудь разумное».
---
### Базовый пример: просим число у пользователя
Что здесь происходит:
-
-
- Цикл повторяется, пока пользователь не введет корректное значение.
Такой подход спасает от падения программы и делает поведение предсказуемым.
---
### Несколько исключений и «план Б» по умолчанию
Иногда полезно различать разные типы ошибок:
- Мы обрабатываем две разные ошибки по‑разному.
- Вместо аварийного завершения возвращаем
---
### Добавляем
---
### Практический мини‑паттерн: «надёжный ввод с попытками»
Здесь мы:
- ограничиваем количество попыток,
- при превышении — явно выбрасываем исключение с понятным текстом.
---
Обработка ошибок с
try / exceptЛюбая программа, в которую что‑то вводит человек, рано или поздно ломается. Пользователь вместо числа вводит
"пять", вместо пути к файлу — пустую строку, а вместо выбора меню — пробел. Если не обработать такие ситуации, программа падает с некрасивым трейсбеком.В Python для этого есть связка
try / except. Идея проста: «Попробуй выполнить этот код. Если случится ошибка — поймай её и сделай что‑нибудь разумное».
---
### Базовый пример: просим число у пользователя
while True:
user_input = input("Enter an integer: ")
try:
value = int(user_input)
break
except ValueError:
print("This is not a valid integer, try again.")
print("You entered:", value)
Что здесь происходит:
-
try: пытаемся преобразовать строку в int.-
except ValueError: ловим конкретную ошибку, когда строка не похожа на число.- Цикл повторяется, пока пользователь не введет корректное значение.
Такой подход спасает от падения программы и делает поведение предсказуемым.
---
### Несколько исключений и «план Б» по умолчанию
Иногда полезно различать разные типы ошибок:
def safe_division(a, b):
try:
return a / b
except ZeroDivisionError:
print("Division by zero is not allowed.")
return None
except TypeError:
print("Both arguments must be numbers.")
return None
print(safe_division(10, 0))
print(safe_division(10, "x"))
- Мы обрабатываем две разные ошибки по‑разному.
- Вместо аварийного завершения возвращаем
None и осмысленное сообщение.---
### Добавляем
else и finallyelse выполняется, если в try не было ошибок. finally выполняется всегда — даже при ошибке или return.def read_int(prompt):
while True:
user_input = input(prompt)
try:
value = int(user_input)
except ValueError:
print("Invalid number, try again.")
else:
print("Thanks, got a valid number.")
return value
finally:
# Можно использовать для логирования, очистки ресурсов и т.п.
pass
---
### Практический мини‑паттерн: «надёжный ввод с попытками»
def ask_int_limited(prompt, attempts=3):
for _ in range(attempts):
try:
return int(input(prompt))
except ValueError:
print("Please enter a valid integer.")
raise ValueError("Too many invalid attempts.")
age = ask_int_limited("Enter your age: ")
print("Age is:", age)
Здесь мы:
- ограничиваем количество попыток,
- при превышении — явно выбрасываем исключение с понятным текстом.
---
Обработка ошибок с
try / except — это не «костыль», а нормальная архитектура. Она превращает хрупкие скрипты в программы, с которыми можно работать без страха, что любой неправильный ввод всё сломает.👍2
Сериализация с
Иногда хочется просто “заморозить” объект Python: сложный словарь, результат обучения модели, кэш вычислений — а потом в другом месте и в другое время “разморозить” его и продолжить работу. Для этого есть модуль
Сериализация — это превращение объекта в последовательность байт. Десериализация — обратный процесс: из байтов снова получаем объект.
- файлы непонятны другим языкам;
- нельзя бездумно грузить непроверенный pickle-файл — это небезопасно.
---
### Базовый пример: сохранить и загрузить объект
---
### Работа в памяти:
Если нужно получить байты, например, чтобы отправить по сети или положить в базу:
---
### Сериализация собственных классов
Важно: при загрузке класс
---
### Вопрос безопасности
Никогда не загружай pickle-файлы из непроверенных источников.
Для обмена данными между разными системами чаще выбирают JSON. Но когда нужно быстро и удобно сохранять сложные Python-объекты “для себя” —
pickle: сохраняем объекты Python “в банку”Иногда хочется просто “заморозить” объект Python: сложный словарь, результат обучения модели, кэш вычислений — а потом в другом месте и в другое время “разморозить” его и продолжить работу. Для этого есть модуль
pickle.Сериализация — это превращение объекта в последовательность байт. Десериализация — обратный процесс: из байтов снова получаем объект.
pickle умеет сохранять почти всё: списки, словари, классы, функции (с нюансами), вложенные структуры. Минус — формат бинарный и специфичный для Python, то есть:- файлы непонятны другим языкам;
- нельзя бездумно грузить непроверенный pickle-файл — это небезопасно.
---
### Базовый пример: сохранить и загрузить объект
import pickle
data = {
"users": ["alice", "bob"],
"settings": {"theme": "dark", "volume": 80},
"active": True
}
# сериализация в файл
with open("data.pkl", "wb") as f:
pickle.dump(data, f)
# десериализация из файла
with open("data.pkl", "rb") as f:
loaded_data = pickle.load(f)
print(loaded_data)
dump/load работают с файлами. Обрати внимание на режим: "wb" и "rb" — бинарный.---
### Работа в памяти:
dumps и loadsЕсли нужно получить байты, например, чтобы отправить по сети или положить в базу:
import pickle
config = {"timeout": 10, "retries": 3}
serialized = pickle.dumps(config) # bytes
print(type(serialized), len(serialized))
restored = pickle.loads(serialized)
print(restored)
---
### Сериализация собственных классов
pickle умеет сохранять экземпляры пользовательских классов (если они определены на верхнем уровне модуля):import pickle
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
player = Player("Alice", 42)
with open("player.pkl", "wb") as f:
pickle.dump(player, f)
with open("player.pkl", "rb") as f:
loaded_player = pickle.load(f)
print(loaded_player.name, loaded_player.score)
Важно: при загрузке класс
Player должен быть доступен (импортирован) с тем же именем и в том же модуле.---
### Вопрос безопасности
pickle.load может выполнить произвольный код, если файл злонамеренно подделан. Поэтому правило простое и жесткое:Никогда не загружай pickle-файлы из непроверенных источников.
Для обмена данными между разными системами чаще выбирают JSON. Но когда нужно быстро и удобно сохранять сложные Python-объекты “для себя” —
pickle это мощный и гибкий инструмент.👍3