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

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

Ir al canal en Telegram

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

Mostrar más
1 239
Suscriptores
Sin datos24 horas
+17 días
-130 días
Archivo de publicaciones
Применение regular expressions для валидации 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, дальше проще разбирать и более сложные шаблоны.

Применение regular expressions для валидации email адреса
Применение regular expressions для валидации email адреса

### Python для начинающих: доступ по паролю с помощью 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‑скриптов это простой и удобный способ реализовать хранение пользователей и сессий без лишних зависимостей.

Работа с ограничением доступа с сессионной памятью shelve
Работа с ограничением доступа с сессионной памятью shelve

Python для начинающих: как 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 = 3status = 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.

Использование enum для создания понятных значений
Использование enum для создания понятных значений

Как укротить дату в Python: форматирование с 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 — месяц, 0112 - %d — день месяца, 0131 - %H — часы (24-часовой формат), 0023 - %M — минуты, 0059 - %S — секунды, 0059 Комбинируя их, можно получить любой стиль:
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, но десятки разных представлений — под логи, интерфейсы, имена файлов и отчеты. Главное — выучить несколько кодов, а остальное легко комбинировать как конструктор.

Как задать формат вывода дат с помощью strftime
Как задать формат вывода дат с помощью strftime

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

Создание локального чат-бота используя модуль socket
Создание локального чат-бота используя модуль socket

Работа с мультипроцессорностью в Python: модуль multiprocessing Если ваш скрипт считает что‑то «тяжёлое» и при этом использует только одно ядро процессора — он просто ленится. Модуль multiprocessing позволяет загрузить все ядра и реально ускорить выполнение CPU‑интенсивных задач. --- ### Почему не threading? В Python есть GIL — глобальная блокировка интерпретатора. Потоки (threading) отлично подходят для I/O (сетевые запросы, файлы), но почти не ускоряют чистые вычисления: байткод всё равно исполняется в один поток. multiprocessing запускает несколько процессов, у каждого свой интерпретатор и свой GIL. Значит, они могут работать параллельно на разных ядрах. --- ### Простой пример: распараллеливаем вычисления Допустим, у нас есть тяжёлая функция:
from multiprocessing import Process
import time

def heavy_job(n):
    s = 0
    for i in range(10_000_000):
        s += (i * n) % 7
    print(f"Result for {n}: {s}")

if __name__ == "__main__":
    t0 = time.time()

    processes = []
    for num in [1, 2, 3, 4]:
        p = Process(target=heavy_job, args=(num,))
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

    print("Time:", time.time() - t0)
Ключевые моменты: - if __name__ == "__main__": обязателен на Windows и macOS, иначе процессы начнут бесконечно плодиться. - Process(target=..., args=...) — создаём процесс и указываем функцию. - start() — запускаем, join() — ждём завершения. Каждый процесс считает свою версию heavy_job, ядра загружаются параллельно. --- ### Используем пул процессов: Pool Чаще всего нужно применить одну функцию к множеству входных данных. Для этого идеален Pool.
from multiprocessing import Pool
import time

def square(x):
    return x * x

if __name__ == "__main__":
    data = list(range(10))

    t0 = time.time()
    with Pool(processes=4) as pool:
        result = pool.map(square, data)
    print("Result:", result)
    print("Time:", time.time() - t0)
Что важно: - Pool сам управляет созданием и завершением процессов. - map работает примерно как встроенная map, только параллельно. - Функция, передаваемая в процессы, должна быть определена на верхнем уровне модуля (не внутри другой функции, не lambda). --- ### Передача данных между процессами У процессов своя память, поэтому обычные переменные не разделяются. Можно использовать: - Queue — безопасная очередь для обмена сообщениями. - Manager — создаёт объекты, которые можно разделять (списки, словари). Пример с Queue:
from multiprocessing import Process, Queue

def worker(q, x):
    q.put(x * 2)

if __name__ == "__main__":
    q = Queue()
    processes = []

    for i in range(5):
        p = Process(target=worker, args=(q, i))
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

    results = [q.get() for _ in range(5)]
    print(results)
--- multiprocessing — это способ превратить ваш скрипт в мини‑кластер на одном компьютере. Если задача упирается в процессор, а не в диск или сеть, имеет смысл попробовать распараллеливание: часто выигрыш по времени оказывается впечатляющим.

Работа с мультипроцессорностью с помощью модуля multiprocessing
Работа с мультипроцессорностью с помощью модуля multiprocessing

### Создаём простой будильник на Python с помощью модуля time Когда только начинаешь учить Python, хочется писать что‑то полезное, а не только складывать числа в консоли. Давай сделаем простой будильник — без графики, без музыки, но с чёткой логикой и полезным знакомством с модулем time. --- ## Модуль time в двух словах Главные функции, которые нам пригодятся: - time.time() — возвращает текущее время в секундах с 1 января 1970 года. - time.sleep(seconds) — “усыпляет” программу на заданное количество секунд. - time.strftime(format) — возвращает текущие дату и время в удобном форматe. --- ## Будильник по времени в формате HH:MM Идея простая: 1. Пользователь вводит время будильника. 2. Программа регулярно проверяет текущее время. 3. Когда часы и минуты совпадают — срабатывает будильник.
import time

def get_current_time_str():
    return time.strftime("%H:%M")

alarm_time = input("Enter alarm time (HH:MM): ")

print(f"Alarm is set for {alarm_time}. Waiting...")

while True:
    now = get_current_time_str()
    if now == alarm_time:
        print("WAKE UP! Alarm time reached!")
        break
    time.sleep(10)
Что здесь важно: - time.strftime("%H:%M") возвращает строку вида 14:05. - Мы сравниваем строку текущего времени с введённой строкой — просто и удобно. - time.sleep(10) снижает нагрузку на процессор: мы проверяем время раз в 10 секунд, а не бесконечно крутим цикл. --- ## Будильник с задержкой в секундах Иногда нужно “разбудить” себя (или программу) через N секунд:
import time

delay = int(input("Enter delay in seconds: "))
print(f"Timer set for {delay} seconds...")

time.sleep(delay)

print("TIME IS UP!")
Здесь мы используем только time.sleep(), но это уже рабочий таймер. --- ## Немного улучшений - Можно сделать звуковой сигнал с помощью системных команд (winsound на Windows или os.system("play ...") на Linux), но это уже следующий шаг. - Можно дать возможность вводить и дату, и время, затем сравнивать через time.strptime() и time.mktime() — получится почти мини‑календарь. --- Мы использовали всего один модуль — time, а уже получили два рабочих инструмента: будильник по часам и таймер по секундам. Отличный пример того, как из простых функций собрать полезный скрипт.

Создание простого будильника с помощью модуля time
Создание простого будильника с помощью модуля time

Работа со временем в Python: tzinfo и pytz без магии и боли Если вы хоть раз пытались работать с часовыми поясами, вы знаете: это ад из смещений, переходов на летнее время и странных правил разных стран. Но Python умеет в это неплохо — если правильно пользоваться tzinfo и библиотекой pytz. --- ### Наивные и осознанные datetime По умолчанию datetime в Python не знает, в каком он часовом поясе.
from datetime import datetime

dt_naive = datetime(2024, 4, 5, 12, 0, 0)
print(dt_naive.tzinfo)  # None
Это наивный объект — он не привязан ни к одному поясу. Любая арифметика и сравнения с другими датами могут быть некорректны, если вы смешиваете разные пояса. --- ### Интерфейс tzinfo Класс tzinfo — это абстракция часового пояса. В теории вы можете написать свой класс, унаследованный от tzinfo, который определит: - utcoffset() — смещение от UTC - dst() — переход на летнее время - tzname() — имя пояса Но на практике руками это почти никогда не делают: слишком много нюансов. Поэтому используется pytz. --- ### Подключаем pytz Устанавливаем:
pip install pytz
Простой пример: берём локальное время в Москве и переводим его в Нью-Йорк.
from datetime import datetime
import pytz

tz_moscow = pytz.timezone("Europe/Moscow")
tz_ny = pytz.timezone("America/New_York")

dt_naive = datetime(2024, 4, 5, 12, 0, 0)

dt_moscow = tz_moscow.localize(dt_naive)
dt_ny = dt_moscow.astimezone(tz_ny)

print(dt_moscow, dt_moscow.tzinfo)  # 2024-04-05 12:00:00+03:00
print(dt_ny, dt_ny.tzinfo)          # 2024-04-05 05:00:00-04:00
Ключевой момент — никогда не делать так:
# ПЛОХО
dt_wrong = datetime(2024, 4, 5, 12, 0, 0, tzinfo=tz_moscow)
С pytz это ломает обработку переходов на летнее время. Нужно именно localize(). --- ### Храним в UTC, показываем пользователю в его поясе Золотое правило: хранить время в UTC, показывать — в локальном часовом поясе.
from datetime import datetime
import pytz

utc = pytz.utc
tz_user = pytz.timezone("Asia/Tokyo")

# допустим, это пришло из БД как UTC
dt_stored = datetime(2024, 4, 5, 9, 0, 0, tzinfo=utc)

dt_user = dt_stored.astimezone(tz_user)
print("UTC:", dt_stored)
print("User time:", dt_user)
Так вы избегаете боли при переносе данных между сервером, БД и пользователями из разных стран. --- ### Подводные камни: неоднозначные и несуществующие времена При переходах на зимнее/летнее время могут быть: - Неоднозначные моменты (1:30 случается дважды) - Несуществующие моменты (стрелка перепрыгивает через 2:00–3:00) pytz умеет это обрабатывать через параметр is_dst:
from datetime import datetime
import pytz

tz = pytz.timezone("America/New_York")
dt_naive = datetime(2024, 11, 3, 1, 30, 0)  # переход на зимнее время

dt_first = tz.localize(dt_naive, is_dst=True)   # "летняя" 1:30
dt_second = tz.localize(dt_naive, is_dst=False) # "зимняя" 1:30

print(dt_first)
print(dt_second)
--- ### Вывод - Используйте tzinfo, но руками его не реализуйте — для реального мира берите pytz. - Делайте localize() для привязки наивного datetime к поясу. - Всегда храните время в UTC, а отображайте в нужной зоне через astimezone(). - Будьте осторожны с переходами на летнее/зимнее время — они реально ломают голову, но pytz знает все правила.

Работа со временем: tzinfo и pytz для часовых поясов
Работа со временем: tzinfo и pytz для часовых поясов

Создание zip-архива и добавление файлов средствами shutil и zipfile Работа с архивами — один из тех навыков, которые рано или поздно нужны любому Python-разработчику. Сделать резервную копию проекта, упаковать отчёты, автоматически отправить архив по почте — всё это удобно делать прямо из кода. В стандартной библиотеке есть два ключевых инструмента: shutil и zipfile. Разберём оба. --- ## Вариант 1: Быстрое сжатие папки через shutil shutil хорош, когда нужно просто взять каталог и превратить его в zip-архив, не вдаваясь в детали.
import shutil
from pathlib import Path

base_dir = Path("project_data")
archive_name = "project_backup"  # без расширения

# Создаст файл project_backup.zip с содержимым папки project_data
archive_path = shutil.make_archive(
    base_name=archive_name,
    format="zip",
    root_dir=base_dir
)

print(f"Archive created: {archive_path}")
Особенности: - Архивируется вся папка целиком. - Почти нет настроек: минимум кода, максимум результата. - Удобно для периодических бэкапов и упаковки готовых проектов. --- ## Вариант 2: Тонкий контроль через zipfile Когда нужно добавлять файлы по одному, менять путь внутри архива, докладывать файлы позже — пригодится zipfile. ### Создание архива и добавление файлов
import zipfile
from pathlib import Path

zip_path = Path("reports.zip")

files_to_add = [
    Path("reports/january.csv"),
    Path("reports/february.csv"),
]

with zipfile.ZipFile(zip_path, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
    for file_path in files_to_add:
        arc_name = file_path.name  # как файл будет называться внутри архива
        zf.write(file_path, arcname=arc_name)

print(f"Created: {zip_path}")
### Дозапись файлов в существующий архив
import zipfile
from pathlib import Path

zip_path = Path("reports.zip")

with zipfile.ZipFile(zip_path, mode="a", compression=zipfile.ZIP_DEFLATED) as zf:
    zf.write("reports/march.csv", arcname="march.csv")
--- ## Чтение и извлечение содержимого
import zipfile

with zipfile.ZipFile("reports.zip", mode="r") as zf:
    print("Files in archive:")
    for name in zf.namelist():
        print(" -", name)

    # Извлечь всё в папку extracted_reports
    zf.extractall("extracted_reports")
--- Когда что использовать? - shutil.make_archive — когда нужно быстро заархивировать каталог целиком. - zipfile.ZipFile — когда нужен контроль: выборочные файлы, особые имена внутри архива, дозапись, чтение без распаковки. Оба модуля входят в стандартную библиотеку, так что никаких дополнительных установок — только импорт и немного аккуратного кода.

Создание zip-архива и добавление файлов средствами shutil и zipfile
Создание zip-архива и добавление файлов средствами shutil и zipfile

Изучаем очередь задач с queue.Queue: безопасный обмен данными между потоками Когда в программе появляется несколько потоков, один из первых вопросов — как безопасно передавать им данные. Глобальные списки и словари быстро превращают код в хаос. Для таких задач в стандартной библиотеке есть герой попроще и понадежнее — queue.Queue. --- ## Что такое queue.Queue? Queue — это потокобезопасная структура данных «первым пришёл — первым вышел» (FIFO). Главные свойства: - безопасна для работы из нескольких потоков; - умеет блокироваться при put() и get(), пока не появится место или элемент; - поддерживает ограничение размера (maxsize), чтобы не «забить» память. Подключается просто:
from queue import Queue
--- ## Базовый пример: очередь задач Допустим, у нас есть «производитель» задач и «потребитель», который их обрабатывает:
from queue import Queue
from threading import Thread
import time

def producer(task_queue, n_tasks):
    for i in range(n_tasks):
        task = f"task_{i}"
        print(f"Produce: {task}")
        task_queue.put(task)  # блокируется, если очередь заполнена
    task_queue.put(None)  # сигнал завершения

def consumer(task_queue):
    while True:
        task = task_queue.get()  # блокируется, пока очередь пуста
        if task is None:  # получили сигнал "конец"
            task_queue.task_done()
            break
        print(f"Consume: {task}")
        time.sleep(0.2)  # имитация работы
        task_queue.task_done()

def main():
    task_queue = Queue(maxsize=5)

    t_prod = Thread(target=producer, args=(task_queue, 10))
    t_cons = Thread(target=consumer, args=(task_queue,))

    t_prod.start()
    t_cons.start()

    task_queue.join()  # ждём, пока все задачи будут обработаны
    t_prod.join()
    t_cons.join()

if __name__ == "__main__":
    main()
Ключевые моменты: - put() и get() по умолчанию блокирующие; - task_done() говорит очереди: «элемент обработан»; - join() блокируется, пока количество task_done() не сравняется с количеством put(). --- ## Неблокирующий режим и таймауты Иногда блокироваться нельзя:
from queue import Queue, Empty, Full

q = Queue(maxsize=2)

try:
    q.put("item1", block=False)
    q.put("item2", block=False)
    q.put("item3", timeout=0.5)  # подождём немного
except Full:
    print("Queue is full!")

try:
    item = q.get(block=False)
    print("Got:", item)
    item = q.get(timeout=0.5)
except Empty:
    print("Queue is empty!")
Так можно аккуратно обрабатывать ситуации, когда очередь переполнена или пуста, без зависаний программы. --- ## Когда использовать queue.Queue - Обработка задач в нескольких потоках (скачивание файлов, парсинг страниц). - Логирование из разных потоков в один обработчик. - Поток-производитель (сбор данных) и поток-потребитель (анализ, запись в БД). Главный плюс: Queue берет на себя всю головную боль с блокировками и синхронизацией. Вам остается думать о логике задач, а не о том, как не устроить гонку данных и дедлок.

Изучение интерфейса для очередей очередей с queue.Queue
Изучение интерфейса для очередей очередей с queue.Queue