Создаем локального чат-бота на 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
Работа с логами: запись и фильтрация с модулем
В любой программе рано или поздно наступает момент, когда
### Базовая настройка логов
Начнем с простого: запишем логи в файл и выведем их в консоль.
Ключевые идеи:
-
-
-
### Логи по модулям
Для реальных приложений полезно иметь отдельный логгер на модуль:
Теперь ты можешь тонко управлять шумом:
Весь проект логируется детально, но
### Фильтрация логов
Иногда нужен не только уровень, но и умный фильтр. Например, логировать только запросы к критичным endpoint’ам.
Фильтры позволяют:
- разделять логи по файлам по любому условию;
- временно “выключать” шумный функционал;
- собирать отдельные журналы для аудита.
### Логирование исключений
Еще один must-have — логировать трассировку стека:
---
Модуль
loggingВ любой программе рано или поздно наступает момент, когда
print() перестает помогать. Нужно понимать, что происходит “под капотом”, но не засорять консоль сотней строк. Здесь на сцену выходит модуль logging.### Базовая настройка логов
Начнем с простого: запишем логи в файл и выведем их в консоль.
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[
logging.FileHandler("app.log", encoding="utf-8"),
logging.StreamHandler()
]
)
logger = logging.getLogger("app")
logger.debug("Debug message") # не увидим, уровень ниже INFO
logger.info("App started")
logger.warning("Low disk space")
logger.error("Unexpected error")
Ключевые идеи:
-
level — минимальный уровень сообщений (DEBUG, INFO, WARNING, ERROR, CRITICAL).-
handlers — куда отправлять логи (файл, консоль, сеть и т.п.).-
format — шаблон строки лога: время, уровень, имя логгера, сообщение.### Логи по модулям
Для реальных приложений полезно иметь отдельный логгер на модуль:
# module_a.py
import logging
logger = logging.getLogger("module_a")
def run_task():
logger.info("Task started")
logger.debug("Internal details...")
# main.py
import logging
import module_a
logging.basicConfig(level=logging.DEBUG)
module_a.run_task()
Теперь ты можешь тонко управлять шумом:
logging.getLogger("module_a").setLevel(logging.WARNING)
Весь проект логируется детально, но
module_a пишет только предупреждения и ошибки.### Фильтрация логов
Иногда нужен не только уровень, но и умный фильтр. Например, логировать только запросы к критичным endpoint’ам.
import logging
class EndpointFilter(logging.Filter):
def filter(self, record):
return "/payments" in record.getMessage()
logger = logging.getLogger("api")
logger.setLevel(logging.INFO)
handler = logging.FileHandler("payments.log", encoding="utf-8")
handler.addFilter(EndpointFilter())
logger.addHandler(handler)
logger.info("GET /status 200")
logger.info("POST /payments 500") # только это уйдет в payments.log
Фильтры позволяют:
- разделять логи по файлам по любому условию;
- временно “выключать” шумный функционал;
- собирать отдельные журналы для аудита.
### Логирование исключений
Еще один must-have — логировать трассировку стека:
import logging
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger("errors")
try:
1 / 0
except ZeroDivisionError:
logger.exception("Calculation failed")
logger.exception автоматически добавит traceback к сообщению — незаменимо при отладке.---
Модуль
logging — это ваш “черный ящик” приложения. Настроив уровни, обработчики и фильтры, вы получаете полную картину происходящего без тонны print() и хаоса в консоли.👍2❤1🔥1