uk
Feedback
Python для начинающих

Python для начинающих

Відкрити в Telegram

Python для начинающих

Показати більше
1 240
Підписники
Немає даних24 години
+17 днів
+130 день
Архів дописів
Создание текстовых отчетов и логов с записью в файл день за днем
Создание текстовых отчетов и логов с записью в файл день за днем

Python для начинающих: узнаём имя компьютера, 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}")
Такой скрипт можно встроить в любой проект: логировать среду запуска, подстраивать поведение под ОС или просто использовать как первый шаг к более серьёзным инструментам администрирования и мониторинга.

Получение системной информации: имя компьютера, IP, ОС
Получение системной информации: имя компьютера, IP, ОС

Сравнение объектов в Python: магия __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, чтобы не писать все методы сравнения вручную. Когда объекты начинают “понимать”, как им сравниваться, код становится проще, а данные — значительно умнее.

Сравнение объектов и реализация собственных методов eq и lt
Сравнение объектов и реализация собственных методов eq и lt

Реализация счетчика посещений сайта на Flask и SQLite Иногда самый полезный функционал — самый простой. Счётчик посещений показывает, что сайт “живой”: им пользуются, к нему возвращаются. Давай сделаем минималистичный, но “правильный” вариант на 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, где показывать не только общее число, но и статистику по дням. - Вынести конфиг базы и пути в отдельный файл настроек. С таким минимальным примером у тебя уже есть полноценный счётчик посещений, который не сбросится при перезапуске сервера и не зависит от сторонних сервисов.

Реализация счетчика посещений сайта с использованием flask и sql
Реализация счетчика посещений сайта с использованием flask и sql

Как создать простую капчу с помощью библиотеки 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-проектов и внутренних инструментов этого более чем достаточно и прекрасно показывает связку: генерация данных → создание изображения → валидация ввода пользователя.

Как создать простую капчу с использованием библиотеки captcha
Как создать простую капчу с использованием библиотеки captcha

Создание коротких ссылок с использованием стороннего API Длинные 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.

Создание коротких ссылок с использованием стороннего API
Создание коротких ссылок с использованием стороннего API

Python для начинающих: генерируем календарные события (ICS-файлы) Хотите, чтобы ваш скрипт сам добавлял события в 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.

Работа с событиями календаря: генерация ICS-файлов
Работа с событиями календаря: генерация ICS-файлов

Python для начинающих: строим граф зависимостей и ищем путь в ширину Представьте, что у вас есть список задач, и каждая зависит от других: нельзя запустить тесты, пока не собран проект, а сборка невозможна без установки зависимостей. Это и есть граф зависимостей — мощная идея, которая лежит в основе систем сборки, планировщиков задач и даже соцсетей. В 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']
Мы только что построили простой "двигатель" зависимостей: по нему можно понять, какие шаги нужно пройти, чтобы выполнить любую цель. Та же идея используется в пакетных менеджерах, планировщиках задач и роутинге графов.

Построение простого графа зависимостей и поиск в ширину
Построение простого графа зависимостей и поиск в ширину

### Асинхронная загрузка данных с помощью 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, вы по-настоящему почувствуете разницу между «программой, которая просто работает» и «программой, которая работает быстро».

Асинхронная загрузка данных с помощью aiohttp
Асинхронная загрузка данных с помощью aiohttp

Подключение к PostgreSQL с psycopg2: базовые операции --------------------------------------------------- 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.

Подключение к PostgreSQL с psycopg2: базовые операции
Подключение к PostgreSQL с psycopg2: базовые операции

Работа со структурами путей с помощью модуля 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 позволяет думать о путях как о объектах с методами, а не как о хрупких строках. Освоив его один раз, вы практически забудете про головную боль с разделителями, относительными путями и ручным разбором имён файлов.