Работа со структурами путей с помощью модуля
Большинство скриптов рано или поздно начинают работать с файлами и папками. И тут новичок упирается в хаос: слэши туда, бэкслэши сюда, Windows, Linux, относительные пути… Модуль
---
### Основы: объект Path
Объект
---
### Соединение путей — как конструктор
Вместо конкатенации строк:
---
### Анализ пути: части, имя, расширение
Это удобно при разборе файлов по расширениям или при генерации новых имен.
---
### Проверка существования и создание директорий
---
### Итерация по файлам и фильтрация
---
### Чтение и запись текста
Никаких явных
---
### Преобразование в абсолютный и реальный путь
Это помогает, когда нужно логировать или передавать пути в другие системы.
---
pathlibБольшинство скриптов рано или поздно начинают работать с файлами и папками. И тут новичок упирается в хаос: слэши туда, бэкслэши сюда, Windows, Linux, относительные пути… Модуль
pathlib решает это красиво и объектно‑ориентированно.---
### Основы: объект Path
from pathlib import Path
base_dir = Path(".") # текущая директория
home_dir = Path.home() # домашняя директория пользователя
project_file = Path("src/main.py")
Объект
Path понимает, где он запущен: под Windows или Linux, и сам подбирает правильные разделители.---
### Соединение путей — как конструктор
Вместо конкатенации строк:
log_dir = Path("logs")
log_file = log_dir / "app.log" # оператор / для склейки путей
log_file станет logs/app.log или logs\app.log — в зависимости от ОС. Никаких ручных слэшей.---
### Анализ пути: части, имя, расширение
path = Path("data/archive/report_2024.csv")
print(path.name) # report_2024.csv
print(path.stem) # report_2024
print(path.suffix) # .csv
print(path.parent) # data/archive
print(list(path.parents)) # все родительские директории
Это удобно при разборе файлов по расширениям или при генерации новых имен.
---
### Проверка существования и создание директорий
reports_dir = Path("reports/2024")
if not reports_dir.exists():
reports_dir.mkdir(parents=True, exist_ok=True)
parents=True создаст все недостающие уровни. Не нужно вручную проверять каждый.---
### Итерация по файлам и фильтрация
from pathlib import Path
data_dir = Path("data")
for csv_file in data_dir.rglob("*.csv"):
print(csv_file, csv_file.stat().st_size, "bytes")
rglob("*.csv") рекурсивно находит все CSV‑файлы. stat() дает информацию о файле (размер, даты и т.д.).---
### Чтение и запись текста
text_file = Path("notes/todo.txt")
text_file.write_text("learn pathlib\nuse it everywhere", encoding="utf-8")
content = text_file.read_text(encoding="utf-8")
print(content)
Никаких явных
open(), всё через удобные методы объекта пути.---
### Преобразование в абсолютный и реальный путь
p = Path("logs/app.log")
print(p.resolve()) # абсолютный путь, с учётом реальной файловой системы
Это помогает, когда нужно логировать или передавать пути в другие системы.
---
pathlib позволяет думать о путях как о объектах с методами, а не как о хрупких строках. Освоив его один раз, вы практически забудете про головную боль с разделителями, относительными путями и ручным разбором имён файлов.👍4
Подключение к PostgreSQL с psycopg2: базовые операции
---------------------------------------------------
PostgreSQL — отличный выбор для первых серьёзных проектов на Python: надёжен, быстр и очень любим разработчиками. А библиотека
### Установка и первое подключение
Устанавливаем библиотеку:
Простейшее подключение:
### Создаём таблицу
Важно: изменения нужно подтверждать через
### Вставка данных (INSERT)
Используем параметризованный запрос — так мы защищаемся от SQL-инъекций:
### Чтение данных (SELECT)
-
- Есть ещё
### Обновление и удаление
### Корректное завершение работы
Лучше оборачивать всё в
---
---------------------------------------------------
PostgreSQL — отличный выбор для первых серьёзных проектов на Python: надёжен, быстр и очень любим разработчиками. А библиотека
psycopg2 — классический способ «подружить» Python с Postgres.### Установка и первое подключение
Устанавливаем библиотеку:
pip install psycopg2-binary
Простейшее подключение:
import psycopg2
conn = psycopg2.connect(
dbname="test_db",
user="test_user",
password="secret_password",
host="localhost",
port=5432,
)
cur = conn.cursor()
conn — это соединение с базой, cur — объект курсора, через который мы выполняем запросы.### Создаём таблицу
cur.execute("""
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL,
age INT
);
""")
conn.commit()
Важно: изменения нужно подтверждать через
conn.commit(). Без этого таблица «не сохранится».### Вставка данных (INSERT)
Используем параметризованный запрос — так мы защищаемся от SQL-инъекций:
insert_query = "INSERT INTO users (username, age) VALUES (%s, %s);"
data = [("alice", 25), ("bob", 30), ("charlie", 22)]
for row in data:
cur.execute(insert_query, row)
conn.commit()
%s — плейсхолдеры, реальные значения передаются вторым аргументом в execute.### Чтение данных (SELECT)
cur.execute("SELECT id, username, age FROM users WHERE age > %s;", (23,))
rows = cur.fetchall()
for row in rows:
user_id, username, age = row
print(user_id, username, age)
-
fetchall() — забрать все строки.- Есть ещё
fetchone() и fetchmany(n) для порций данных.### Обновление и удаление
cur.execute(
"UPDATE users SET age = age + 1 WHERE username = %s;",
("alice",)
)
cur.execute(
"DELETE FROM users WHERE username = %s;",
("charlie",)
)
conn.commit()
### Корректное завершение работы
cur.close()
conn.close()
Лучше оборачивать всё в
try/finally или использовать контекстные менеджеры, чтобы соединение точно закрывалось.---
psycopg2 даёт низкоуровневый, но очень прозрачный контроль над запросами. Освоив эти базовые операции — CREATE, INSERT, SELECT, UPDATE, DELETE и работу с транзакциями через commit — вы уже можете строить реальные приложения на Python + PostgreSQL.🔥4👍2
### Асинхронная загрузка данных с помощью
Представьте, что вам нужно скачать данные сразу с десятка API. Вариант «скачать по очереди» работает, но ощущается как очередь в одинокую кассу в супермаркете. Асинхронность в Python — это открытие сразу десятка касс. И один из самых удобных инструментов для этого — библиотека
---
## Почему обычный
Классический код:
Каждый запрос ждёт ответа, блокируя программу. Если каждый URL отвечает 3 секунды, а их 10 — вы получите ~30 секунд ожидания.
---
## Основная идея асинхронности
Вместо того чтобы залипать в ожидании ответа, мы даём Python возможность:
- отправить запрос,
- до ответа заняться другими задачами,
- вернуться к запросу, когда данные готовы.
Для этого используются
---
## Загрузка с
Установим библиотеку:
Простой пример параллельной загрузки:
Ключевые моменты:
-
-
-
-
Если каждый запрос «висит» 3 секунды, то при таком подходе общее время будет примерно те же 3 секунды, а не 9.
---
## Обработка ошибок и таймауты
Асинхронный код тоже должен уметь падать красиво:
Что здесь важно:
-
-
- Ошибки не ломают программу, а превращаются в аккуратные объекты с описанием.
---
Асинхронная загрузка с
aiohttp: ускоряемся по-взросломуПредставьте, что вам нужно скачать данные сразу с десятка API. Вариант «скачать по очереди» работает, но ощущается как очередь в одинокую кассу в супермаркете. Асинхронность в Python — это открытие сразу десятка касс. И один из самых удобных инструментов для этого — библиотека
aiohttp.---
## Почему обычный
requests медленныйКлассический код:
import requests
urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/3",
]
def fetch_all(urls):
data = []
for url in urls:
resp = requests.get(url)
data.append(resp.text)
return data
Каждый запрос ждёт ответа, блокируя программу. Если каждый URL отвечает 3 секунды, а их 10 — вы получите ~30 секунд ожидания.
---
## Основная идея асинхронности
Вместо того чтобы залипать в ожидании ответа, мы даём Python возможность:
- отправить запрос,
- до ответа заняться другими задачами,
- вернуться к запросу, когда данные готовы.
Для этого используются
async / await и событийный цикл asyncio.---
## Загрузка с
aiohttp: базовый примерУстановим библиотеку:
pip install aiohttp
Простой пример параллельной загрузки:
import asyncio
import aiohttp
urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/3",
]
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def fetch_all(urls):
async with aiohttp.ClientSession() as session:
tasks = []
for url in urls:
task = asyncio.create_task(fetch(session, url))
tasks.append(task)
results = await asyncio.gather(*tasks)
return results
if __name__ == "__main__":
data = asyncio.run(fetch_all(urls))
for i, content in enumerate(data, start=1):
print(f"Response {i} length:", len(content))
Ключевые моменты:
-
async def создаёт асинхронную функцию.-
await говорит: «подожди результат, но не блокируй остальную программу».-
asyncio.create_task запускает корутину параллельно с другими.-
asyncio.gather ждёт, пока завершатся все задачи.Если каждый запрос «висит» 3 секунды, то при таком подходе общее время будет примерно те же 3 секунды, а не 9.
---
## Обработка ошибок и таймауты
Асинхронный код тоже должен уметь падать красиво:
import asyncio
import aiohttp
from aiohttp import ClientError
async def fetch_safe(session, url, timeout=5):
try:
async with session.get(url, timeout=timeout) as response:
response.raise_for_status()
return await response.json()
except (asyncio.TimeoutError, ClientError) as e:
return {"url": url, "error": str(e)}
async def main():
urls = [
"https://httpbin.org/json",
"https://httpbin.org/status/404",
"https://example.com:9999", # likely error
]
async with aiohttp.ClientSession() as session:
tasks = [asyncio.create_task(fetch_safe(session, url)) for url in urls]
results = await asyncio.gather(*tasks)
for item in results:
print(item)
if __name__ == "__main__":
asyncio.run(main())
Что здесь важно:
-
timeout защищает от «вечного ожидания».-
response.raise_for_status() поднимает исключение при кодах 4xx/5xx.- Ошибки не ломают программу, а превращаются в аккуратные объекты с описанием.
---
Асинхронная загрузка с
aiohttp особенно полезна при работе с API, парсинге сайтов и любых задачах, где узкое место — сеть. Освоив aiohttp, вы по-настоящему почувствуете разницу между «программой, которая просто работает» и «программой, которая работает быстро».👍5
Python для начинающих: строим граф зависимостей и ищем путь в ширину
Представьте, что у вас есть список задач, и каждая зависит от других: нельзя запустить тесты, пока не собран проект, а сборка невозможна без установки зависимостей. Это и есть граф зависимостей — мощная идея, которая лежит в основе систем сборки, планировщиков задач и даже соцсетей.
В Python граф удобно представить как словарь: вершина → список соседей.
Но часто нам нужно обратное направление: от задачи к её зависимостям. Переделаем:
Теперь цель: узнать, в каком порядке запускать задачи, чтобы не нарушить зависимости. Для начала разберём более простую вещь — поиск в ширину (BFS): он обходит граф "волнами" от стартовой вершины, сначала ближайшие, затем более дальние.
Классический BFS работает через очередь:
Проверим на нашем графе зависимостей:
Этот порядок не гарантирует строгой "сначала зависимости, потом задача", но BFS уже даёт важное:
мы находим все связанные задачи и делаем это без зацикливания.
Теперь усложним: хотим узнать, нужно ли выполнить
Используем BFS как поиск пути:
Мы только что построили простой "двигатель" зависимостей:
по нему можно понять, какие шаги нужно пройти, чтобы выполнить любую цель.
Та же идея используется в пакетных менеджерах, планировщиках задач и роутинге графов.
Представьте, что у вас есть список задач, и каждая зависит от других: нельзя запустить тесты, пока не собран проект, а сборка невозможна без установки зависимостей. Это и есть граф зависимостей — мощная идея, которая лежит в основе систем сборки, планировщиков задач и даже соцсетей.
В Python граф удобно представить как словарь: вершина → список соседей.
graph = {
"build": ["test"],
"install_deps": ["build"],
"lint": ["test"],
"test": [],
}
Но часто нам нужно обратное направление: от задачи к её зависимостям. Переделаем:
deps = {
"test": ["build", "lint"],
"build": ["install_deps"],
"lint": [],
"install_deps": []
}
Теперь цель: узнать, в каком порядке запускать задачи, чтобы не нарушить зависимости. Для начала разберём более простую вещь — поиск в ширину (BFS): он обходит граф "волнами" от стартовой вершины, сначала ближайшие, затем более дальние.
Классический BFS работает через очередь:
from collections import deque
def bfs(graph, start):
visited = set()
order = []
queue = deque([start])
while queue:
node = queue.popleft()
if node in visited:
continue
visited.add(node)
order.append(node)
for neighbor in graph[node]:
if neighbor not in visited:
queue.append(neighbor)
return order
Проверим на нашем графе зависимостей:
print(bfs(deps, "test"))
# Возможный результат: ['test', 'build', 'lint', 'install_deps']
Этот порядок не гарантирует строгой "сначала зависимости, потом задача", но BFS уже даёт важное:
мы находим все связанные задачи и делаем это без зацикливания.
Теперь усложним: хотим узнать, нужно ли выполнить
install_deps, чтобы добраться до test. Используем BFS как поиск пути:
def bfs_path(graph, start, goal):
from collections import deque
queue = deque([[start]])
visited = set()
while queue:
path = queue.popleft()
node = path[-1]
if node == goal:
return path
if node in visited:
continue
visited.add(node)
for neighbor in graph[node]:
if neighbor not in visited:
new_path = path + [neighbor]
queue.append(new_path)
return None
print(bfs_path(deps, "test", "install_deps"))
# ['test', 'build', 'install_deps']
Мы только что построили простой "двигатель" зависимостей:
по нему можно понять, какие шаги нужно пройти, чтобы выполнить любую цель.
Та же идея используется в пакетных менеджерах, планировщиках задач и роутинге графов.
🔥3👍1
Python для начинающих: генерируем календарные события (ICS-файлы)
Хотите, чтобы ваш скрипт сам добавлял события в Google Calendar, Outlook или Apple Calendar? Для этого не нужен их API — достаточно сгенерировать простой текстовый файл формата ICS и пользователь сможет импортировать его двойным кликом.
Формат ICS — это обычный текст по стандарту iCalendar. Пример минимального события:
Сгенерируем такой файл на Python «вручную» — без сторонних библиотек.
После запуска рядом появится
Несколько важных моментов:
- Используйте
- Поля
-
Если хочется расписание насыщеннее, можно добавить описание и место:
Так вы можете генерировать приглашения на вебинары, напоминания о дедлайнах и даже целые расписания — и все это обычными строками в Python.
Хотите, чтобы ваш скрипт сам добавлял события в Google Calendar, Outlook или Apple Calendar? Для этого не нужен их API — достаточно сгенерировать простой текстовый файл формата ICS и пользователь сможет импортировать его двойным кликом.
Формат ICS — это обычный текст по стандарту iCalendar. Пример минимального события:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//MyApp//Calendar 1.0//EN
BEGIN:VEVENT
UID:123@example.com
DTSTAMP:20250115T120000Z
DTSTART:20250120T090000Z
DTEND:20250120T100000Z
SUMMARY:Morning meeting
END:VEVENT
END:VCALENDAR
Сгенерируем такой файл на Python «вручную» — без сторонних библиотек.
from datetime import datetime, timedelta, timezone
from uuid import uuid4
def format_dt(dt: datetime) -> str:
# Преобразуем в формат YYYYMMDDTHHMMSSZ
return dt.astimezone(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
def create_ics_event(summary: str, start: datetime, end: datetime) -> str:
uid = f"{uuid4()}@example.com"
dtstamp = format_dt(datetime.now(timezone.utc))
lines = [
"BEGIN:VCALENDAR",
"VERSION:2.0",
"PRODID:-//PythonDemo//ICS Generator//EN",
"BEGIN:VEVENT",
f"UID:{uid}",
f"DTSTAMP:{dtstamp}",
f"DTSTART:{format_dt(start)}",
f"DTEND:{format_dt(end)}",
f"SUMMARY:{summary}",
"END:VEVENT",
"END:VCALENDAR",
]
return "\r\n".join(lines) + "\r\n"
if __name__ == "__main__":
start_dt = datetime(2025, 1, 20, 9, 0, tzinfo=timezone.utc)
end_dt = start_dt + timedelta(hours=1)
ics_content = create_ics_event("Morning meeting", start_dt, end_dt)
with open("meeting.ics", "w", encoding="utf-8") as f:
f.write(ics_content)
После запуска рядом появится
meeting.ics. Откройте его — система предложит добавить событие в календарь.Несколько важных моментов:
- Используйте
\r\n в качестве перевода строк — этого требует стандарт.- Поля
DTSTART, DTEND, DTSTAMP желательно указывать в UTC (с суффиксом Z).-
UID должен быть уникальным для события — удобно использовать uuid4().Если хочется расписание насыщеннее, можно добавить описание и место:
def create_ics_event_with_details(summary, start, end, description, location):
base = create_ics_event(summary, start, end).split("\r\n")
# Вставляем поля перед END:VEVENT
insert_index = base.index("END:VEVENT")
base.insert(insert_index, f"DESCRIPTION:{description}")
base.insert(insert_index, f"LOCATION:{location}")
return "\r\n".join(base) + "\r\n"
Так вы можете генерировать приглашения на вебинары, напоминания о дедлайнах и даже целые расписания — и все это обычными строками в Python.
👍3❤1
Создание коротких ссылок с использованием стороннего API
Длинные URL — зло. Их неудобно отправлять в мессенджерах, сложно запоминать, а выглядят они как случайный набор символов. Давай сделаем свой мини‑«сократитель ссылок» на Python, используя сторонний API.
В качестве примера возьмем бесплатный сервис shrtco.de. Он предоставляет простой HTTP‑API для сокращения ссылок.
---
### Что нам понадобится
1. Модуль
2. Любая обычная ссылка, которую нужно сократить.
Установим
---
### Базовый пример: сокращаем одну ссылку
Что здесь происходит:
-
-
- Из поля
---
### Обработка ошибок и проверка входных данных
Интернет не идеален: иногда API падает, иногда пользователь передает ерунду вместо ссылки. Добавим простую обертку с обработкой ошибок:
---
### Мини‑утилита для списка ссылок
Теперь сделаем маленький скрипт, который умеет сокращать несколько ссылок сразу:
Так можно быстро подготовить короткие ссылки, например, для рассылки или описания проекта.
---
В итоге ты познакомился с базовой схемой работы с внешним API: отправили запрос, получили JSON, вытащили нужные поля, обработали ошибки. Дальше можно развивать идею: сделать CLI‑утилиту, интеграцию в телеграм‑бота или даже собственную веб‑страницу‑сократитель на Flask или FastAPI.
Длинные URL — зло. Их неудобно отправлять в мессенджерах, сложно запоминать, а выглядят они как случайный набор символов. Давай сделаем свой мини‑«сократитель ссылок» на Python, используя сторонний API.
В качестве примера возьмем бесплатный сервис shrtco.de. Он предоставляет простой HTTP‑API для сокращения ссылок.
---
### Что нам понадобится
1. Модуль
requests для отправки HTTP‑запросов.2. Любая обычная ссылка, которую нужно сократить.
Установим
requests (если еще не установлен):pip install requests
---
### Базовый пример: сокращаем одну ссылку
import requests
def shorten_url(long_url: str) -> str:
api_url = "https://api.shrtco.de/v2/shorten"
params = {"url": long_url}
response = requests.get(api_url, params=params, timeout=10)
response.raise_for_status() # выбросит исключение, если статус не 200
data = response.json()
if not data.get("ok"):
raise ValueError(f"API error: {data}")
return data["result"]["full_short_link"]
if __name__ == "__main__":
long_url = "https://www.python.org/doc/"
short_url = shorten_url(long_url)
print(f"Original: {long_url}")
print(f"Short: {short_url}")
Что здесь происходит:
-
requests.get отправляет GET‑запрос на API.-
response.json() превращает ответ сервера в словарь Python.- Из поля
result["full_short_link"] берем уже готовую короткую ссылку.---
### Обработка ошибок и проверка входных данных
Интернет не идеален: иногда API падает, иногда пользователь передает ерунду вместо ссылки. Добавим простую обертку с обработкой ошибок:
def safe_shorten_url(long_url: str) -> str:
if not long_url.startswith(("http://", "https://")):
raise ValueError("URL must start with http:// or https://")
try:
return shorten_url(long_url)
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
return long_url # в худшем случае вернем исходный URL
except ValueError as e:
print(f"API responded with error: {e}")
return long_url
---
### Мини‑утилита для списка ссылок
Теперь сделаем маленький скрипт, который умеет сокращать несколько ссылок сразу:
def batch_shorten(urls: list[str]) -> dict[str, str]:
result = {}
for url in urls:
short = safe_shorten_url(url)
result[url] = short
return result
if __name__ == "__main__":
urls = [
"https://docs.python.org/3/library/",
"https://pypi.org/project/requests/",
"https://www.djangoproject.com/",
]
mapping = batch_shorten(urls)
for original, short in mapping.items():
print(f"{original} -> {short}")
Так можно быстро подготовить короткие ссылки, например, для рассылки или описания проекта.
---
В итоге ты познакомился с базовой схемой работы с внешним API: отправили запрос, получили JSON, вытащили нужные поля, обработали ошибки. Дальше можно развивать идею: сделать CLI‑утилиту, интеграцию в телеграм‑бота или даже собственную веб‑страницу‑сократитель на Flask или FastAPI.
👍3
Как создать простую капчу с помощью библиотеки
Капча — это маленький страж, который защищает формы на сайте от ботов: регистрация, вход, отправка заявки. Давай разберёмся, как всего за несколько строк на Python сделать свою простую капчу в виде картинки с текстом.
### Установка библиотеки
Нам понадобится модуль
Основной класс, с которым будем работать, —
### Генерация изображения капчи
Сгенерируем картинку с кодом и сохраним её в файл:
Что здесь происходит:
-
-
- Картинка сохраняется как
### Проверка ответа пользователя
Обычно логика следующая: мы генерируем капчу, сохраняем код в сессии (или временно в памяти), затем сравниваем его с вводом пользователя.
Упрощённый пример без веб-фреймворка:
### Идеи для улучшения
- Увеличить длину кода (
- Генерировать разные размеры картинок (
- Хранить коды не в памяти, а, например, в Redis или сессии веб-фреймворка.
Такая капча не заменит сложные промышленные решения, но для учебных проектов, pet-проектов и внутренних инструментов этого более чем достаточно и прекрасно показывает связку: генерация данных → создание изображения → валидация ввода пользователя.
captchaКапча — это маленький страж, который защищает формы на сайте от ботов: регистрация, вход, отправка заявки. Давай разберёмся, как всего за несколько строк на Python сделать свою простую капчу в виде картинки с текстом.
### Установка библиотеки
Нам понадобится модуль
captcha:pip install captcha
Основной класс, с которым будем работать, —
ImageCaptcha.### Генерация изображения капчи
Сгенерируем картинку с кодом и сохраним её в файл:
from captcha.image import ImageCaptcha
import random
import string
def generate_code(length: int = 5) -> str:
symbols = string.ascii_uppercase + string.digits
return ''.join(random.choice(symbols) for _ in range(length))
def generate_captcha(image_path: str = "captcha.png") -> str:
image_captcha = ImageCaptcha(width=200, height=80)
code = generate_code()
image = image_captcha.generate_image(code)
image.save(image_path)
return code
if __name__ == "__main__":
correct_code = generate_captcha()
print("Captcha code:", correct_code)
Что здесь происходит:
-
generate_code создаёт случайный набор из букв и цифр.-
ImageCaptcha рисует картинку с этим кодом.- Картинка сохраняется как
captcha.png, а правильный код возвращается для дальнейшей проверки.### Проверка ответа пользователя
Обычно логика следующая: мы генерируем капчу, сохраняем код в сессии (или временно в памяти), затем сравниваем его с вводом пользователя.
Упрощённый пример без веб-фреймворка:
def verify_captcha(user_input: str, correct_code: str) -> bool:
return user_input.strip().upper() == correct_code.upper()
if __name__ == "__main__":
correct_code = generate_captcha()
user_input = input("Enter captcha from image: ")
if verify_captcha(user_input, correct_code):
print("Access granted")
else:
print("Access denied")
### Идеи для улучшения
- Увеличить длину кода (
length=6–7), чтобы усложнить перебор.- Генерировать разные размеры картинок (
width, height) под дизайн сайта.- Хранить коды не в памяти, а, например, в Redis или сессии веб-фреймворка.
Такая капча не заменит сложные промышленные решения, но для учебных проектов, pet-проектов и внутренних инструментов этого более чем достаточно и прекрасно показывает связку: генерация данных → создание изображения → валидация ввода пользователя.
👍3
Реализация счетчика посещений сайта на Flask и SQLite
Иногда самый полезный функционал — самый простой. Счётчик посещений показывает, что сайт “живой”: им пользуются, к нему возвращаются. Давай сделаем минималистичный, но “правильный” вариант на Flask + SQLite.
### Архитектура идеи
Нам нужно:
1. Веб-приложение на Flask.
2. База данных SQLite с таблицей
3. Логика: при заходе на страницу увеличиваем число посещений и показываем пользователю.
Почему SQLite?
- Ничего устанавливать отдельно не нужно — база хранится в одном файле.
- Для маленьких проектов и пет-проектов более чем достаточно.
### Создаём базу и таблицу
Файл
Этот скрипт:
- создаёт файл
- гарантирует единственную строку с
Запусти его один раз:
### Flask-приложение
Файл
Что здесь важно:
- Отдельная функция
-
- В
### Возможные улучшения
- Сохранять IP пользователя, User-Agent и дату визита в отдельной таблице
- Добавить отдельную админ-страницу
- Вынести конфиг базы и пути в отдельный файл настроек.
С таким минимальным примером у тебя уже есть полноценный счётчик посещений, который не сбросится при перезапуске сервера и не зависит от сторонних сервисов.
Иногда самый полезный функционал — самый простой. Счётчик посещений показывает, что сайт “живой”: им пользуются, к нему возвращаются. Давай сделаем минималистичный, но “правильный” вариант на Flask + SQLite.
### Архитектура идеи
Нам нужно:
1. Веб-приложение на Flask.
2. База данных SQLite с таблицей
visits. 3. Логика: при заходе на страницу увеличиваем число посещений и показываем пользователю.
Почему SQLite?
- Ничего устанавливать отдельно не нужно — база хранится в одном файле.
- Для маленьких проектов и пет-проектов более чем достаточно.
### Создаём базу и таблицу
Файл
init_db.py:import sqlite3
def init_db():
conn = sqlite3.connect("stats.db")
cursor = conn.cursor()
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS visits (
id INTEGER PRIMARY KEY CHECK (id = 1),
counter INTEGER NOT NULL
)
"""
)
cursor.execute("INSERT OR IGNORE INTO visits (id, counter) VALUES (1, 0)")
conn.commit()
conn.close()
if __name__ == "__main__":
init_db()
Этот скрипт:
- создаёт файл
stats.db,- гарантирует единственную строку с
id = 1, где живёт наш счётчик.Запусти его один раз:
python init_db.py.### Flask-приложение
Файл
app.py:from flask import Flask
import sqlite3
app = Flask(__name__)
def get_db_connection():
conn = sqlite3.connect("stats.db")
conn.row_factory = sqlite3.Row
return conn
def increment_counter():
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("UPDATE visits SET counter = counter + 1 WHERE id = 1")
conn.commit()
cursor.execute("SELECT counter FROM visits WHERE id = 1")
row = cursor.fetchone()
conn.close()
return row["counter"]
@app.route("/")
def index():
visits = increment_counter()
return f"<h1>Welcome!</h1><p>This page was visited {visits} times.</p>"
if __name__ == "__main__":
app.run(debug=True)
Что здесь важно:
- Отдельная функция
get_db_connection() — хороший тон, код легче масштабировать.-
increment_counter() инкапсулирует всю работу с базой: обновление и чтение.- В
index() мы просто вызываем логику и выводим результат.### Возможные улучшения
- Сохранять IP пользователя, User-Agent и дату визита в отдельной таблице
visits_log.- Добавить отдельную админ-страницу
/stats, где показывать не только общее число, но и статистику по дням.- Вынести конфиг базы и пути в отдельный файл настроек.
С таким минимальным примером у тебя уже есть полноценный счётчик посещений, который не сбросится при перезапуске сервера и не зависит от сторонних сервисов.
🔥4
Сравнение объектов в Python: магия
Пока мы сравниваем только числа и строки, все просто:
---
### Базовое сравнение: что делает Python по умолчанию
Если у класса не определен
---
### Даем объектам смысл: реализуем
Сделаем так, чтобы точки считались равными, если равны их координаты:
Ключевой момент — возвращать
---
### Порядок имеет значение: реализуем
Метод
Теперь
---
###
Если реализовать все сравнения вручную (
Достаточно реализовать
---
### Итог
1. По умолчанию объекты сравниваются по идентичности, а не по содержимому.
2.
3. Возвращайте
4. Используйте
Когда объекты начинают “понимать”, как им сравниваться, код становится проще, а данные — значительно умнее.
__eq__ и __lt__Пока мы сравниваем только числа и строки, все просто:
==, <, >, и жизнь удалась. Проблемы начинаются, когда у нас появляются собственные классы: User, Order, Point и т.д. Как Python должен решать, что два объекта равны? Или какой из них “меньше”? По умолчанию — никак полезно для нас.---
### Базовое сравнение: что делает Python по умолчанию
Если у класса не определен
__eq__, выражение a == b для двух объектов одного класса проверяет, один и тот же ли это объект в памяти. Два разных, но “по смыслу” одинаковых объекта будут считаться неравными:class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # False
---
### Даем объектам смысл: реализуем
__eq__Сделаем так, чтобы точки считались равными, если равны их координаты:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(2, 3)
print(p1 == p2) # True
print(p1 == p3) # False
Ключевой момент — возвращать
NotImplemented, если объект другого типа. Тогда Python попробует обратное сравнение или корректно вернёт False.---
### Порядок имеет значение: реализуем
__lt__Метод
__lt__ (“less than”) нужен для операторов < и для сортировки. Например, хотим сортировать точки по расстоянию от начала координат:import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self):
return math.hypot(self.x, self.y)
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
def __lt__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.distance() < other.distance()
points = [Point(3, 4), Point(1, 1), Point(0, 5)]
points.sort()
for p in points:
print(p.x, p.y)
Теперь
sort() понимает, как сравнивать объекты Point.---
###
functools.total_ordering: меньше кода – больше порядкаЕсли реализовать все сравнения вручную (
__lt__, __le__, __gt__, __ge__), код раздуется. Модуль functools умеет помочь:from functools import total_ordering
import math
@total_ordering
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self):
return math.hypot(self.x, self.y)
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
def __lt__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.distance() < other.distance()
Достаточно реализовать
__eq__ и один из методов порядка (__lt__, __le__, __gt__ или __ge__) — остальные создат total_ordering.---
### Итог
1. По умолчанию объекты сравниваются по идентичности, а не по содержимому.
2.
__eq__ отвечает за смысл равенства, __lt__ — за порядок (и сортировку). 3. Возвращайте
NotImplemented при сравнении с чужими типами. 4. Используйте
functools.total_ordering, чтобы не писать все методы сравнения вручную.Когда объекты начинают “понимать”, как им сравниваться, код становится проще, а данные — значительно умнее.
👍3❤1
Python для начинающих: узнаём имя компьютера, IP и ОС
Иногда хочется, чтобы программа знала, где она вообще запущена: как зовут компьютер, какой у него IP-адрес и под какой ОС всё это работает. Это важно для логирования, сетевых скриптов, настройки путей и просто для самодиагностики.
Сегодня разберём три стандартных модуля:
---
## Имя компьютера и IP-адрес
Модуль
Проблема:
Интернет реально не используется, но сокету нужно «представиться» и он раскрывает наш реальный адрес в сети.
---
## Определяем операционную систему
Здесь король — модуль
Если нужна «человеческая» строка одним махом:
---
## Немного о пользователе и окружении
Модуль
---
## Собираем всё вместе
Мини-скрипт системной самодиагностики:
Такой скрипт можно встроить в любой проект: логировать среду запуска, подстраивать поведение под ОС или просто использовать как первый шаг к более серьёзным инструментам администрирования и мониторинга.
Иногда хочется, чтобы программа знала, где она вообще запущена: как зовут компьютер, какой у него IP-адрес и под какой ОС всё это работает. Это важно для логирования, сетевых скриптов, настройки путей и просто для самодиагностики.
Сегодня разберём три стандартных модуля:
socket, platform и os. Никаких сторонних библиотек, всё есть «из коробки».---
## Имя компьютера и IP-адрес
Модуль
socket умеет работать с сетью и при этом спокойно подскажет, как зовут машину и какой у неё IP.import socket
hostname = socket.gethostname()
ip_address = socket.gethostbyname(hostname)
print(f"Hostname: {hostname}")
print(f"IP address: {ip_address}")
Проблема:
gethostbyname иногда возвращает 127.0.0.1, если система так настроена. Более надёжный способ — открыть «фальшивое» соединение наружу и посмотреть, с какого адреса мы выходим:import socket
def get_local_ip():
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]
print("Local IP:", get_local_ip())
Интернет реально не используется, но сокету нужно «представиться» и он раскрывает наш реальный адрес в сети.
---
## Определяем операционную систему
Здесь король — модуль
platform. Он даёт как короткую, так и детальную информацию.import platform
print("System:", platform.system()) # 'Windows', 'Linux', 'Darwin'
print("Release:", platform.release()) # версия системы
print("Version:", platform.version()) # детальная строка
print("Machine:", platform.machine()) # архитектура (x86_64, arm64...)
Если нужна «человеческая» строка одним махом:
import platform
info = platform.platform()
print("Platform info:", info)
---
## Немного о пользователе и окружении
Модуль
os помогает заглянуть в окружение: путь к пользователю, имя юзера и прочие системные переменные.import os
user = os.getenv("USERNAME") or os.getenv("USER")
home_dir = os.path.expanduser("~")
print(f"User: {user}")
print(f"Home directory: {home_dir}")
---
## Собираем всё вместе
Мини-скрипт системной самодиагностики:
import socket
import platform
import os
def get_local_ip():
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]
def get_system_info():
hostname = socket.gethostname()
ip_address = get_local_ip()
system = platform.system()
release = platform.release()
user = os.getenv("USERNAME") or os.getenv("USER")
return {
"hostname": hostname,
"ip": ip_address,
"system": system,
"release": release,
"user": user,
}
if __name__ == "__main__":
info = get_system_info()
for key, value in info.items():
print(f"{key}: {value}")
Такой скрипт можно встроить в любой проект: логировать среду запуска, подстраивать поведение под ОС или просто использовать как первый шаг к более серьёзным инструментам администрирования и мониторинга.
👍5
Создание текстовых отчетов и логов с записью в файл день за днем
Рано или поздно любой скрипт вырастает до состояния: «Хочу понимать, что он делал вчера ночью». Тут на сцену выходят отчеты и логи. Давайте разберем пару рабочих приемов: ежедневные файлы логов и простые текстовые отчеты.
---
## Ежедневный лог-файл
Частая практика — один файл лога на день:
Сделаем простую функцию-логгер:
Что здесь важно:
-
- Режим
- Имя файла зависит от даты — архив логов сам формируется по дням.
---
## Простой текстовый отчет по результатам работы
Представим скрипт, который что-то обрабатывает и в конце дня создает итоговый отчет: сколько задач прошло, сколько упало, какой процент успеха.
Фишки:
- Отчет — обычный текстовый файл, его легко открыть в любом редакторе или отправить по почте.
- Формат предельно простой, но уже дает картину дня.
- Логгер и отчет связаны: лог фиксирует факт создания отчета.
---
## Идея для развития
Дальше можно:
- добавлять в отчет «ТОП-5 ошибок дня»;
- архивировать старые логи в zip;
- использовать модуль
Но фундамент один: аккуратная, ежедневная запись в файлы с понятными именами и структурой. Это уже делает ваш код ближе к «боевому» уровню.
Рано или поздно любой скрипт вырастает до состояния: «Хочу понимать, что он делал вчера ночью». Тут на сцену выходят отчеты и логи. Давайте разберем пару рабочих приемов: ежедневные файлы логов и простые текстовые отчеты.
---
## Ежедневный лог-файл
Частая практика — один файл лога на день:
log_2025-01-21.txt, log_2025-01-22.txt и т.д. Сделаем простую функцию-логгер:
from datetime import datetime
from pathlib import Path
LOG_DIR = Path("logs")
LOG_DIR.mkdir(exist_ok=True)
def get_log_file_path() -> Path:
today_str = datetime.now().strftime("%Y-%m-%d")
return LOG_DIR / f"log_{today_str}.txt"
def log_message(level: str, message: str) -> None:
log_file = get_log_file_path()
time_str = datetime.now().strftime("%H:%M:%S")
line = f"[{time_str}] [{level.upper()}] {message}\n"
with log_file.open("a", encoding="utf-8") as f:
f.write(line)
# Пример использования
log_message("info", "Script started")
log_message("warning", "Low disk space")
log_message("error", "Failed to connect to server")
Что здесь важно:
-
Path из pathlib удобнее обычных строк для путей.- Режим
"a" — дозапись в конец файла, не стирая старые логи.- Имя файла зависит от даты — архив логов сам формируется по дням.
---
## Простой текстовый отчет по результатам работы
Представим скрипт, который что-то обрабатывает и в конце дня создает итоговый отчет: сколько задач прошло, сколько упало, какой процент успеха.
from datetime import datetime
from pathlib import Path
REPORT_DIR = Path("reports")
REPORT_DIR.mkdir(exist_ok=True)
def save_daily_report(total: int, success: int, failed: int) -> Path:
today_str = datetime.now().strftime("%Y-%m-%d")
report_path = REPORT_DIR / f"report_{today_str}.txt"
success_rate = (success / total * 100) if total else 0
lines = [
f"Report date: {today_str}",
f"Total tasks: {total}",
f"Successful: {success}",
f"Failed: {failed}",
f"Success rate: {success_rate:.2f}%",
]
with report_path.open("w", encoding="utf-8") as f:
f.write("\n".join(lines))
return report_path
# Пример
report_file = save_daily_report(total=120, success=110, failed=10)
log_message("info", f"Daily report saved to {report_file}")
Фишки:
- Отчет — обычный текстовый файл, его легко открыть в любом редакторе или отправить по почте.
- Формат предельно простой, но уже дает картину дня.
- Логгер и отчет связаны: лог фиксирует факт создания отчета.
---
## Идея для развития
Дальше можно:
- добавлять в отчет «ТОП-5 ошибок дня»;
- архивировать старые логи в zip;
- использовать модуль
logging, чтобы прокачать формат и уровни логов.Но фундамент один: аккуратная, ежедневная запись в файлы с понятными именами и структурой. Это уже делает ваш код ближе к «боевому» уровню.
👍3