Создание PDF-документов с помощью FPDF: просто, как print()
PDF — идеальный формат для чеков, отчетов, сертификатов и даже мини-отчетов по учебным проектам. В Python один из самых понятных инструментов для их создания — библиотека fpdf2 (современный форк FPDF).
---
### Установка
Базовый объект —
---
### Первый PDF за 10 строк
Создадим простой одностраничный документ с заголовком и текстом.
Ключевые моменты:
-
-
-
-
-
---
### Простой “отчет”: таблица в PDF
Сделаем мини-таблицу, например, список задач.
Здесь:
- Используем список кортежей как источник данных.
- Меняем стиль шрифта для заголовка таблицы.
-
---
### Добавление изображения
Например, логотип в шапке:
---
FPDF удобна тем, что работает “как конструктор”: просто добавляете текст, таблицы, картинки, постепенно превращая Python-скрипт в генератор аккуратных PDF-документов. Отличный навык для автоматизации отчетов и учебных проектов.
PDF — идеальный формат для чеков, отчетов, сертификатов и даже мини-отчетов по учебным проектам. В Python один из самых понятных инструментов для их создания — библиотека fpdf2 (современный форк FPDF).
---
### Установка
pip install fpdf2
Базовый объект —
FPDF, с ним и будем работать.---
### Первый PDF за 10 строк
Создадим простой одностраничный документ с заголовком и текстом.
from fpdf import FPDF
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", size=16)
pdf.cell(0, 10, "Hello, PDF world!", ln=1, align="C")
pdf.set_font("Arial", size=12)
text = "This is a simple PDF generated with fpdf2 in Python."
pdf.multi_cell(0, 8, text)
pdf.output("example_basic.pdf")
Ключевые моменты:
-
add_page() — добавляем страницу.-
set_font() — выбираем шрифт и размер.-
cell() — строка текста; ln=1 переносит курсор на новую строку.-
multi_cell() — блок текста с переносами.-
output() — сохраняем файл.---
### Простой “отчет”: таблица в PDF
Сделаем мини-таблицу, например, список задач.
from fpdf import FPDF
data = [
("ID", "Task", "Status"),
("1", "Learn FPDF basics", "Done"),
("2", "Generate simple report", "In progress"),
("3", "Share PDF with team", "Pending"),
]
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", "B", 14)
pdf.cell(0, 10, "Tasks Report", ln=1, align="C")
pdf.ln(4)
pdf.set_font("Arial", size=11)
col_widths = [15, 90, 30]
for row_index, row in enumerate(data):
for col_index, text in enumerate(row):
style = "B" if row_index == 0 else ""
pdf.set_font("Arial", style, 11)
pdf.cell(col_widths[col_index], 8, text, border=1)
pdf.ln(8)
pdf.output("tasks_report.pdf")
Здесь:
- Используем список кортежей как источник данных.
- Меняем стиль шрифта для заголовка таблицы.
-
border=1 рисует рамки ячеек — уже похоже на отчет.---
### Добавление изображения
Например, логотип в шапке:
from fpdf import FPDF
pdf = FPDF()
pdf.add_page()
pdf.image("logo.png", x=10, y=8, w=30)
pdf.set_font("Arial", "B", 16)
pdf.cell(0, 10, "Company Report", ln=1, align="C")
pdf.output("report_with_logo.pdf")
---
FPDF удобна тем, что работает “как конструктор”: просто добавляете текст, таблицы, картинки, постепенно превращая Python-скрипт в генератор аккуратных PDF-документов. Отличный навык для автоматизации отчетов и учебных проектов.
👍3❤1
Введение в работу с INI‑файлами с модулем
Когда настройки программы начинают расползаться по коду в виде "магических констант", наступает хаос. Гораздо удобнее хранить конфиг в отдельном файле — компактно, читаемо и без лишних зависимостей. Для этого в Python есть стандартный модуль
### Как выглядит INI‑файл
Типичный
Секции в квадратных скобках, ниже — пары
### Чтение INI с
Полезные методы:
-
-
- квадратные скобки
### Значения по умолчанию
Если нужного ключа нет — можно задать дефолт:
Так код не упадет, даже если в INI забыли прописать опцию.
### Создание и запись INI‑файла
Секции и ключи ведут себя почти как обычные словари.
### Переменные и интерполяция
Если интерполяция не нужна, можно выключить:
---
configparserКогда настройки программы начинают расползаться по коду в виде "магических констант", наступает хаос. Гораздо удобнее хранить конфиг в отдельном файле — компактно, читаемо и без лишних зависимостей. Для этого в Python есть стандартный модуль
configparser, который работает с INI‑файлами.### Как выглядит INI‑файл
Типичный
settings.ini:[database]
host = localhost
port = 5432
user = admin
[app]
debug = true
log_level = INFO
Секции в квадратных скобках, ниже — пары
ключ = значение.### Чтение INI с
configparserimport configparser
config = configparser.ConfigParser()
config.read("settings.ini")
db_host = config["database"]["host"]
db_port = config.getint("database", "port")
debug_mode = config.getboolean("app", "debug")
print(db_host, db_port, debug_mode)
Полезные методы:
-
get() — строка (по умолчанию)-
getint(), getfloat(), getboolean() — автоматическое приведение типов- квадратные скобки
config["section"]["key"] — быстрый доступ как к словарю### Значения по умолчанию
Если нужного ключа нет — можно задать дефолт:
log_level = config.get("app", "log_level", fallback="WARNING")
Так код не упадет, даже если в INI забыли прописать опцию.
### Создание и запись INI‑файла
import configparser
config = configparser.ConfigParser()
config["database"] = {
"host": "localhost",
"port": "3306",
"user": "root"
}
config["app"] = {}
config["app"]["debug"] = "false"
config["app"]["log_level"] = "INFO"
with open("generated.ini", "w") as config_file:
config.write(config_file)
Секции и ключи ведут себя почти как обычные словари.
### Переменные и интерполяция
configparser умеет подставлять значения из других опций:[paths]
base_dir = /var/app
logs_dir = %(base_dir)s/logs
import configparser
config = configparser.ConfigParser()
config.read("paths.ini")
logs_dir = config["paths"]["logs_dir"]
print(logs_dir) # /var/app/logs
Если интерполяция не нужна, можно выключить:
config = configparser.ConfigParser(interpolation=None)
---
configparser — простой способ вынести настройки из кода, не таща в проект JSON/YAML и сторонние библиотеки. Для маленьких и средних приложений INI‑файл нередко оказывается приятным и достаточным форматом конфигурации.👍3
Создаем простой чат-сервер на сокетах: от нуля до мини-мессенджера
Сокеты — это базовый способ общения программ по сети. Без них не было бы ни браузеров, ни мессенджеров, ни онлайн-игр. Давай сделаем свой простой чат-сервер на Python и поймем, как это работает изнутри.
---
## Базовая идея
Нам нужны две части:
1. Сервер — ждет подключения клиентов и пересылает сообщения.
2. Клиент — подключается к серверу и отправляет/получает сообщения.
Используем стандартный модуль
---
## Простой сервер
Этот сервер:
- принимает несколько клиентов,
- читает от каждого сообщения,
- рассылает их всем остальным.
Ключевые моменты:
-
-
- Для каждого клиента создаем поток, чтобы они не блокировали друг друга.
---
## Простой клиент
Подключаемся к серверу и читаем/пишем сообщения одновременно.
---
## Что можно улучшить дальше
- Добавить никнеймы и формат сообщений (
- Логировать чаты в файл.
- Использовать
Но уже сейчас ты сделал основу своего мини-мессенджера на чистых сокетах — без магии, только протокол TCP и немного кода.
Сокеты — это базовый способ общения программ по сети. Без них не было бы ни браузеров, ни мессенджеров, ни онлайн-игр. Давай сделаем свой простой чат-сервер на Python и поймем, как это работает изнутри.
---
## Базовая идея
Нам нужны две части:
1. Сервер — ждет подключения клиентов и пересылает сообщения.
2. Клиент — подключается к серверу и отправляет/получает сообщения.
Используем стандартный модуль
socket, ничего дополнительно устанавливать не нужно.---
## Простой сервер
Этот сервер:
- принимает несколько клиентов,
- читает от каждого сообщения,
- рассылает их всем остальным.
import socket
import threading
HOST = "127.0.0.1"
PORT = 5000
clients = []
def broadcast(message, sender_sock):
for client in clients:
if client is not sender_sock:
try:
client.sendall(message)
except OSError:
pass
def handle_client(client_sock, addr):
print(f"Connected: {addr}")
while True:
try:
data = client_sock.recv(1024)
if not data:
break
broadcast(data, client_sock)
except ConnectionResetError:
break
print(f"Disconnected: {addr}")
clients.remove(client_sock)
client_sock.close()
def run_server():
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.bind((HOST, PORT))
server_sock.listen()
print(f"Server listening on {HOST}:{PORT}")
while True:
client_sock, addr = server_sock.accept()
clients.append(client_sock)
thread = threading.Thread(target=handle_client, args=(client_sock, addr), daemon=True)
thread.start()
if __name__ == "__main__":
run_server()
Ключевые моменты:
-
socket.AF_INET, socket.SOCK_STREAM — TCP-соединение по IPv4.-
listen() — сервер готов принимать соединения.- Для каждого клиента создаем поток, чтобы они не блокировали друг друга.
---
## Простой клиент
Подключаемся к серверу и читаем/пишем сообщения одновременно.
import socket
import threading
HOST = "127.0.0.1"
PORT = 5000
def receive_messages(sock):
while True:
try:
data = sock.recv(1024)
if not data:
print("Connection closed by server")
break
print(">>", data.decode("utf-8"))
except OSError:
break
def run_client():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
print("Connected to chat server")
thread = threading.Thread(target=receive_messages, args=(sock,), daemon=True)
thread.start()
try:
while True:
msg = input()
if msg.lower() == "/quit":
break
sock.sendall(msg.encode("utf-8"))
finally:
sock.close()
if __name__ == "__main__":
run_client()
---
## Что можно улучшить дальше
- Добавить никнеймы и формат сообщений (
[user]: text).- Логировать чаты в файл.
- Использовать
asyncio вместо потоков для масштабируемости.Но уже сейчас ты сделал основу своего мини-мессенджера на чистых сокетах — без магии, только протокол TCP и немного кода.
👍3
Изучаем простейшие графы с помощью модуля
Когда слышишь «графы», кажется, что это что‑то из теории алгоритмов для олимпиадников. Но в Python уже есть готовый инструмент для работы с простыми графами — модуль
### Что за модуль
В
- проверять, есть ли циклы;
- выполнять топологическую сортировку (определять порядок выполнения задач с зависимостями);
- пошагово «выстреливать» вершины, которые уже можно обработать.
Граф здесь задаётся как зависимости: указываем, какие вершины зависят от каких.
### Простейший пример: порядок задач
Представим, что нам нужно выстроить порядок задач с зависимостями:
Вывод будет чем‑то вроде:
Мы получили корректный порядок выполнения без единого
Важно: если в графе есть цикл,
### Пошаговая обработка: имитация планировщика
Методы:
-
-
-
-
Так можно, например, отправлять независимые задачи в разные потоки или процессы.
### Мини‑валидатор зависимостей
Ещё один практичный сценарий — проверка корректности конфигураций:
Так всего парой строк можно защититься от «замкнувшихся» настроек.
---
graphlibКогда слышишь «графы», кажется, что это что‑то из теории алгоритмов для олимпиадников. Но в Python уже есть готовый инструмент для работы с простыми графами — модуль
graphlib, который появился в версии 3.9. И использовать его можно прямо «из коробки», без сторонних библиотек.### Что за модуль
graphlib?В
graphlib есть класс TopologicalSorter. Он работает с ориентированными ациклическими графами (DAG) и умеет:- проверять, есть ли циклы;
- выполнять топологическую сортировку (определять порядок выполнения задач с зависимостями);
- пошагово «выстреливать» вершины, которые уже можно обработать.
Граф здесь задаётся как зависимости: указываем, какие вершины зависят от каких.
### Простейший пример: порядок задач
Представим, что нам нужно выстроить порядок задач с зависимостями:
from graphlib import TopologicalSorter
graph = {
"cook_pasta": {"buy_products"},
"make_sauce": {"buy_products"},
"serve_dinner": {"cook_pasta", "make_sauce"},
"buy_products": set(),
}
ts = TopologicalSorter(graph)
order = list(ts.static_order())
print(order)
Вывод будет чем‑то вроде:
['buy_products', 'cook_pasta', 'make_sauce', 'serve_dinner']
Мы получили корректный порядок выполнения без единого
if и хитрых циклов.Важно: если в графе есть цикл,
static_order() выбросит CycleError. Это удобно для проверки зависимостей.### Пошаговая обработка: имитация планировщика
TopologicalSorter можно использовать как мини‑движок для планировщика задач:from graphlib import TopologicalSorter
graph = {
"download_data": set(),
"clean_data": {"download_data"},
"train_model": {"clean_data"},
"evaluate_model": {"train_model"},
}
ts = TopologicalSorter(graph)
ts.prepare()
while ts.is_active():
ready_tasks = ts.get_ready()
for task in ready_tasks:
print(f"Running: {task}")
# Здесь могла бы быть реальная работа
ts.done(task)
Методы:
-
prepare() — инициализация процесса;-
get_ready() — вернуть все вершины, у которых уже выполнены зависимости;-
done(node) — сообщить, что вершина обработана;-
is_active() — есть ли ещё работа.Так можно, например, отправлять независимые задачи в разные потоки или процессы.
### Мини‑валидатор зависимостей
Ещё один практичный сценарий — проверка корректности конфигураций:
from graphlib import TopologicalSorter, CycleError
def validate_dependencies(dep_graph: dict) -> bool:
try:
TopologicalSorter(dep_graph).static_order()
return True
except CycleError as e:
print("Cycle detected:", e)
return False
deps_ok = {
"A": {"B"},
"B": {"C"},
"C": set(),
}
deps_bad = {
"A": {"B"},
"B": {"C"},
"C": {"A"},
}
print(validate_dependencies(deps_ok)) # True
print(validate_dependencies(deps_bad)) # False + сообщение о цикле
Так всего парой строк можно защититься от «замкнувшихся» настроек.
---
graphlib — отличный вход в мир графов: минимум теории, максимум практики. Вы просто описываете зависимости, а Python берёт на себя сложную часть — поиск порядка и ловлю циклов.👍3🔥1
Создание временного файла с модулем
Работа с файлами — обычное дело в Python: логи, отчёты, экспорт данных. Но часто файл нужен «на пару секунд» — что‑то записать, передать другой функции, а потом забыть. Создавать для этого реальные файлы в папке проекта неудобно и небезопасно. Здесь на сцену выходит модуль
---
### Простейший временный файл
Функция
Ключевые моменты:
-
-
-
-
---
### Временный файл без имени
Если имя файла не нужно (например, вы просто временно храните данные в пределах одного процесса), используйте
Такой файл может вообще не иметь «нормального» имени в файловой системе (зависит от ОС), зато всё ещё ведёт себя как обычный файловый объект.
---
### Где хранятся временные файлы?
Посмотреть каталог, который использует
Обычно это что‑то вроде
---
### Зачем это нужно на практике?
- Генерация отчёта, который тут же отправляется по сети.
- Временное сохранение загруженного файла перед обработкой.
- Тестирование кода, работающего с файловой системой, без мусора в проекте.
tempfileРабота с файлами — обычное дело в Python: логи, отчёты, экспорт данных. Но часто файл нужен «на пару секунд» — что‑то записать, передать другой функции, а потом забыть. Создавать для этого реальные файлы в папке проекта неудобно и небезопасно. Здесь на сцену выходит модуль
tempfile.tempfile отвечает за создание временных файлов и папок в системном временном каталоге (подбирается автоматически под вашу ОС). Его главный плюс — безопасность и автоматическая уборка за собой.---
### Простейший временный файл
Функция
NamedTemporaryFile создаёт временный файл с именем, к которому можно обратиться как к обычному файлу:import tempfile
with tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".txt") as tmp_file:
tmp_file.write("Some temporary data\n")
tmp_file.flush() # сбрасываем буфер на диск
print("Temp file name:", tmp_file.name)
tmp_file.seek(0)
content = tmp_file.read()
print("Content:", content)
# Здесь файл уже удалён автоматически
Ключевые моменты:
-
mode="w+" — можно и писать, и читать.-
delete=True — файл будет удалён при выходе из with.-
suffix=".txt" — можно задать расширение (полезно для сторонних программ, ожидающих конкретный тип файла).-
tmp_file.name — реальный путь к файлу на диске.---
### Временный файл без имени
Если имя файла не нужно (например, вы просто временно храните данные в пределах одного процесса), используйте
TemporaryFile:import tempfile
with tempfile.TemporaryFile(mode="w+") as tmp:
tmp.write("Internal buffer\n")
tmp.seek(0)
print(tmp.read())
Такой файл может вообще не иметь «нормального» имени в файловой системе (зависит от ОС), зато всё ещё ведёт себя как обычный файловый объект.
---
### Где хранятся временные файлы?
Посмотреть каталог, который использует
tempfile, можно так:import tempfile
print(tempfile.gettempdir())
Обычно это что‑то вроде
/tmp на Linux или C:\Users\<user>\AppData\Local\Temp на Windows.---
### Зачем это нужно на практике?
- Генерация отчёта, который тут же отправляется по сети.
- Временное сохранение загруженного файла перед обработкой.
- Тестирование кода, работающего с файловой системой, без мусора в проекте.
tempfile позволяет писать код, который не оставляет следов, не конфликтует с существующими файлами и не требует ручной уборки. Для аккуратных Python‑разработчиков — must‑have инструмент.👍2
Изучаем очередь задач с
Когда в программе появляется несколько потоков, один из первых вопросов — как безопасно передавать им данные. Глобальные списки и словари быстро превращают код в хаос. Для таких задач в стандартной библиотеке есть герой попроще и понадежнее —
---
## Что такое
Главные свойства:
- безопасна для работы из нескольких потоков;
- умеет блокироваться при
- поддерживает ограничение размера (maxsize), чтобы не «забить» память.
Подключается просто:
---
## Базовый пример: очередь задач
Допустим, у нас есть «производитель» задач и «потребитель», который их обрабатывает:
Ключевые моменты:
-
-
-
---
## Неблокирующий режим и таймауты
Иногда блокироваться нельзя:
Так можно аккуратно обрабатывать ситуации, когда очередь переполнена или пуста, без зависаний программы.
---
## Когда использовать
- Обработка задач в нескольких потоках (скачивание файлов, парсинг страниц).
- Логирование из разных потоков в один обработчик.
- Поток-производитель (сбор данных) и поток-потребитель (анализ, запись в БД).
Главный плюс:
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 берет на себя всю головную боль с блокировками и синхронизацией. Вам остается думать о логике задач, а не о том, как не устроить гонку данных и дедлок.👍2🔥1
Создание zip-архива и добавление файлов средствами
Работа с архивами — один из тех навыков, которые рано или поздно нужны любому Python-разработчику. Сделать резервную копию проекта, упаковать отчёты, автоматически отправить архив по почте — всё это удобно делать прямо из кода. В стандартной библиотеке есть два ключевых инструмента:
---
## Вариант 1: Быстрое сжатие папки через
Особенности:
- Архивируется вся папка целиком.
- Почти нет настроек: минимум кода, максимум результата.
- Удобно для периодических бэкапов и упаковки готовых проектов.
---
## Вариант 2: Тонкий контроль через
Когда нужно добавлять файлы по одному, менять путь внутри архива, докладывать файлы позже — пригодится
### Создание архива и добавление файлов
### Дозапись файлов в существующий архив
---
## Чтение и извлечение содержимого
---
Когда что использовать?
-
-
Оба модуля входят в стандартную библиотеку, так что никаких дополнительных установок — только импорт и немного аккуратного кода.
shutil и zipfileРабота с архивами — один из тех навыков, которые рано или поздно нужны любому Python-разработчику. Сделать резервную копию проекта, упаковать отчёты, автоматически отправить архив по почте — всё это удобно делать прямо из кода. В стандартной библиотеке есть два ключевых инструмента:
shutil и zipfile. Разберём оба.---
## Вариант 1: Быстрое сжатие папки через
shutilshutil хорош, когда нужно просто взять каталог и превратить его в 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 — когда нужен контроль: выборочные файлы, особые имена внутри архива, дозапись, чтение без распаковки.Оба модуля входят в стандартную библиотеку, так что никаких дополнительных установок — только импорт и немного аккуратного кода.
👍3
Работа со временем в Python: tzinfo и pytz без магии и боли
Если вы хоть раз пытались работать с часовыми поясами, вы знаете: это ад из смещений, переходов на летнее время и странных правил разных стран. Но Python умеет в это неплохо — если правильно пользоваться
---
### Наивные и осознанные
По умолчанию
Это наивный объект — он не привязан ни к одному поясу. Любая арифметика и сравнения с другими датами могут быть некорректны, если вы смешиваете разные пояса.
---
### Интерфейс tzinfo
Класс
-
-
-
Но на практике руками это почти никогда не делают: слишком много нюансов. Поэтому используется
---
### Подключаем
Устанавливаем:
Простой пример: берём локальное время в Москве и переводим его в Нью-Йорк.
Ключевой момент — никогда не делать так:
С
---
### Храним в UTC, показываем пользователю в его поясе
Золотое правило: хранить время в UTC, показывать — в локальном часовом поясе.
Так вы избегаете боли при переносе данных между сервером, БД и пользователями из разных стран.
---
### Подводные камни: неоднозначные и несуществующие времена
При переходах на зимнее/летнее время могут быть:
- Неоднозначные моменты (1:30 случается дважды)
- Несуществующие моменты (стрелка перепрыгивает через 2:00–3:00)
---
### Вывод
- Используйте
- Делайте
- Всегда храните время в UTC, а отображайте в нужной зоне через
- Будьте осторожны с переходами на летнее/зимнее время — они реально ломают голову, но
Если вы хоть раз пытались работать с часовыми поясами, вы знаете: это ад из смещений, переходов на летнее время и странных правил разных стран. Но 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 знает все правила.👍2
### Создаём простой будильник на Python с помощью модуля
Когда только начинаешь учить Python, хочется писать что‑то полезное, а не только складывать числа в консоли. Давай сделаем простой будильник — без графики, без музыки, но с чёткой логикой и полезным знакомством с модулем
---
## Модуль
Главные функции, которые нам пригодятся:
-
-
-
---
## Будильник по времени в формате
Идея простая:
1. Пользователь вводит время будильника.
2. Программа регулярно проверяет текущее время.
3. Когда часы и минуты совпадают — срабатывает будильник.
Что здесь важно:
-
- Мы сравниваем строку текущего времени с введённой строкой — просто и удобно.
-
---
## Будильник с задержкой в секундах
Иногда нужно “разбудить” себя (или программу) через N секунд:
Здесь мы используем только
---
## Немного улучшений
- Можно сделать звуковой сигнал с помощью системных команд (
- Можно дать возможность вводить и дату, и время, затем сравнивать через
---
Мы использовали всего один модуль —
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, а уже получили два рабочих инструмента: будильник по часам и таймер по секундам. Отличный пример того, как из простых функций собрать полезный скрипт.👍4
Работа с мультипроцессорностью в Python: модуль
Если ваш скрипт считает что‑то «тяжёлое» и при этом использует только одно ядро процессора — он просто ленится. Модуль
---
### Почему не
В Python есть GIL — глобальная блокировка интерпретатора. Потоки (
---
### Простой пример: распараллеливаем вычисления
Допустим, у нас есть тяжёлая функция:
Ключевые моменты:
-
-
-
Каждый процесс считает свою версию
---
### Используем пул процессов:
Чаще всего нужно применить одну функцию к множеству входных данных. Для этого идеален
Что важно:
-
-
- Функция, передаваемая в процессы, должна быть определена на верхнем уровне модуля (не внутри другой функции, не lambda).
---
### Передача данных между процессами
У процессов своя память, поэтому обычные переменные не разделяются. Можно использовать:
-
-
Пример с
---
multiprocessingЕсли ваш скрипт считает что‑то «тяжёлое» и при этом использует только одно ядро процессора — он просто ленится. Модуль
multiprocessing позволяет загрузить все ядра и реально ускорить выполнение CPU‑интенсивных задач.---
### Почему не
threading?В Python есть GIL — глобальная блокировка интерпретатора. Потоки (
threading) отлично подходят для I/O (сетевые запросы, файлы), но почти не ускоряют чистые вычисления: байткод всё равно исполняется в один поток.multiprocessing запускает несколько процессов, у каждого свой интерпретатор и свой GIL. Значит, они могут работать параллельно на разных ядрах.---
### Простой пример: распараллеливаем вычисления
Допустим, у нас есть тяжёлая функция:
from multiprocessing import Process
import time
def heavy_job(n):
s = 0
for i in range(10_000_000):
s += (i * n) % 7
print(f"Result for {n}: {s}")
if __name__ == "__main__":
t0 = time.time()
processes = []
for num in [1, 2, 3, 4]:
p = Process(target=heavy_job, args=(num,))
p.start()
processes.append(p)
for p in processes:
p.join()
print("Time:", time.time() - t0)
Ключевые моменты:
-
if __name__ == "__main__": обязателен на Windows и macOS, иначе процессы начнут бесконечно плодиться. -
Process(target=..., args=...) — создаём процесс и указываем функцию. -
start() — запускаем, join() — ждём завершения. Каждый процесс считает свою версию
heavy_job, ядра загружаются параллельно.---
### Используем пул процессов:
PoolЧаще всего нужно применить одну функцию к множеству входных данных. Для этого идеален
Pool.from multiprocessing import Pool
import time
def square(x):
return x * x
if __name__ == "__main__":
data = list(range(10))
t0 = time.time()
with Pool(processes=4) as pool:
result = pool.map(square, data)
print("Result:", result)
print("Time:", time.time() - t0)
Что важно:
-
Pool сам управляет созданием и завершением процессов.-
map работает примерно как встроенная map, только параллельно.- Функция, передаваемая в процессы, должна быть определена на верхнем уровне модуля (не внутри другой функции, не lambda).
---
### Передача данных между процессами
У процессов своя память, поэтому обычные переменные не разделяются. Можно использовать:
-
Queue — безопасная очередь для обмена сообщениями.-
Manager — создаёт объекты, которые можно разделять (списки, словари).Пример с
Queue:from multiprocessing import Process, Queue
def worker(q, x):
q.put(x * 2)
if __name__ == "__main__":
q = Queue()
processes = []
for i in range(5):
p = Process(target=worker, args=(q, i))
p.start()
processes.append(p)
for p in processes:
p.join()
results = [q.get() for _ in range(5)]
print(results)
---
multiprocessing — это способ превратить ваш скрипт в мини‑кластер на одном компьютере. Если задача упирается в процессор, а не в диск или сеть, имеет смысл попробовать распараллеливание: часто выигрыш по времени оказывается впечатляющим.👍3
Создаем локального чат-бота на Python с помощью
Локальный чат-бот — это отличный способ понять, как устроены сетевые приложения: клиент, сервер, обмен сообщениями. Без веб-фреймворков и магии — только
---
### Идея
Мы сделаем простой консольный чат-бот:
- Сервер: ждет подключений, принимает сообщения и отвечает.
- Клиент: подключается к боту и общается с ним в одном терминале.
Работать будем только на
---
### Шаг 1. Простейший сервер-бот
Сервер слушает порт, принимает соединение и в цикле отвечает пользователю.
---
### Шаг 2. Клиент для общения с ботом
---
### Что здесь важно понять
1.
2.
3.
4. Локальный бот можно усложнять:
- добавлять обработку команд,
- сохранять историю диалога,
- подключать простейший ИИ или правила.
Такой минималистичный проект отлично прокачивает понимание сетей и одновременно дает живой, работающий результат — своего собственного чат-бота, полностью под вашим контролем.
socketЛокальный чат-бот — это отличный способ понять, как устроены сетевые приложения: клиент, сервер, обмен сообщениями. Без веб-фреймворков и магии — только
socket и немного логики.---
### Идея
Мы сделаем простой консольный чат-бот:
- Сервер: ждет подключений, принимает сообщения и отвечает.
- Клиент: подключается к боту и общается с ним в одном терминале.
Работать будем только на
localhost, без выхода в интернет.---
### Шаг 1. Простейший сервер-бот
Сервер слушает порт, принимает соединение и в цикле отвечает пользователю.
# server.py
import socket
HOST = "127.0.0.1"
PORT = 5000
def generate_reply(message: str) -> str:
message = message.lower()
if "hello" in message:
return "Hi! I'm your local socket bot."
if "help" in message:
return "Try: hello, time, bye."
if "time" in message:
from datetime import datetime
return f"Current time: {datetime.now().strftime('%H:%M:%S')}"
if "bye" in message:
return "Goodbye! Closing connection."
return "I don't understand. Type 'help'."
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen(1)
print(f"Bot is listening on {HOST}:{PORT}...")
conn, addr = s.accept()
with conn:
print(f"Connected by {addr}")
while True:
data = conn.recv(1024)
if not data:
break
user_msg = data.decode("utf-8")
reply = generate_reply(user_msg)
conn.sendall(reply.encode("utf-8"))
if "bye" in user_msg.lower():
break
---
### Шаг 2. Клиент для общения с ботом
# client.py
import socket
HOST = "127.0.0.1"
PORT = 5000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
print("Connected to local bot. Type messages, 'bye' to exit.")
while True:
user_msg = input("> ")
s.sendall(user_msg.encode("utf-8"))
data = s.recv(1024)
bot_reply = data.decode("utf-8")
print("Bot:", bot_reply)
if "bye" in user_msg.lower():
break
---
### Что здесь важно понять
1.
socket.AF_INET, socket.SOCK_STREAM — обычный TCP-сокет.2.
bind + listen + accept — базовый цикл сервера.3.
sendall и recv — протокол обмена байтами; мы сами решаем, во что их превращать (здесь — строки UTF-8).4. Локальный бот можно усложнять:
- добавлять обработку команд,
- сохранять историю диалога,
- подключать простейший ИИ или правила.
Такой минималистичный проект отлично прокачивает понимание сетей и одновременно дает живой, работающий результат — своего собственного чат-бота, полностью под вашим контролем.
👍4❤1