Изучаем таймеры и замеры времени с модулем
Когда код работает медленно, интуиция часто подводит. Кажется, что «вот это место точно тормозит», а на деле время утекает в другом участке. Здесь на сцену выходит модуль
### Почему не
Наивный подход:
Проблемы:
- одно измерение: могли попасть на фоновую нагрузку системы;
- точность зависит от платформы;
- неудобно сравнивать несколько вариантов.
### Базовый пример использования
Минимальный пример из консоли:
Параметры важны:
- первый аргумент — строка с кодом, который измеряем;
-
Но писать код в строках не всегда удобно. Лучше использовать функции и параметр
Так проще рефакторить и отлаживать.
### Сравниваем два варианта кода
Предположим, мы хотим понять, что быстрее: список или генератор в
На малых диапазонах разница может быть микроскопической, но тенденцию видно: списки часто чуть медленнее, потому что требуют дополнительной памяти.
### Использование
Если нужна подготовка данных, её не стоит включать в измеряемый код. Для этого есть
Здесь:
- в
- в
### Немного удобства:
Чтобы понять разброс по времени, используйте
Так можно ориентироваться на минимум как на «лучшие условия» выполнения.
---
timeitКогда код работает медленно, интуиция часто подводит. Кажется, что «вот это место точно тормозит», а на деле время утекает в другом участке. Здесь на сцену выходит модуль
timeit — простой способ точно измерить, что действительно медленнее, а что быстрее.### Почему не
time.time()?Наивный подход:
import time
start = time.time()
result = sum(range(10_000_000))
end = time.time()
print(end - start)
Проблемы:
- одно измерение: могли попасть на фоновую нагрузку системы;
- точность зависит от платформы;
- неудобно сравнивать несколько вариантов.
timeit решает это: он запускает код много раз, считает среднее время и делает это максимально «чисто».### Базовый пример использования
Минимальный пример из консоли:
import timeit
t = timeit.timeit("sum(range(1000))", number=10000)
print(t)
Параметры важны:
- первый аргумент — строка с кодом, который измеряем;
-
number — сколько раз выполнить этот код.Но писать код в строках не всегда удобно. Лучше использовать функции и параметр
stmt как объект:import timeit
def calc():
return sum(range(1000))
t = timeit.timeit(stmt=calc, number=10000)
print(t)
Так проще рефакторить и отлаживать.
### Сравниваем два варианта кода
Предположим, мы хотим понять, что быстрее: список или генератор в
sum.import timeit
def use_list():
return sum([i for i in range(1000)])
def use_gen():
return sum(i for i in range(1000))
t_list = timeit.timeit(use_list, number=10000)
t_gen = timeit.timeit(use_gen, number=10000)
print("list:", t_list)
print("gen :", t_gen)
На малых диапазонах разница может быть микроскопической, но тенденцию видно: списки часто чуть медленнее, потому что требуют дополнительной памяти.
### Использование
setupЕсли нужна подготовка данных, её не стоит включать в измеряемый код. Для этого есть
setup:import timeit
setup_code = "data = list(range(10000))"
stmt_code = """
result = 0
for x in data:
result += x
"""
t = timeit.timeit(stmt=stmt_code, setup=setup_code, number=1000)
print(t)
Здесь:
- в
setup мы генерируем данные один раз для каждого прогона timeit;- в
stmt считаем время только самого алгоритма.### Немного удобства:
repeatЧтобы понять разброс по времени, используйте
repeat:import timeit
def func():
return sum(range(10_000))
times = timeit.repeat(func, number=1000, repeat=5)
print(times) # список из 5 измерений
print("best:", min(times))
Так можно ориентироваться на минимум как на «лучшие условия» выполнения.
---
timeit — это карманный профилировщик для микробенчмарков. Он не заменяет полноценный профилировщик, но идеально подходит, когда нужно честно ответить на вопрос: «Этот способ действительно быстрее, или мне только кажется?»🔥4
Как использовать модуль
Когда файлов становится много, а отправить нужно «всё и сразу», на сцену выходит модуль
---
### Создаем ZIP‑архив из одного файла
Минимальный пример:
Что здесь происходит:
-
-
-
---
### Добавляем несколько файлов и папку
Чтобы пройтись по директории и заархивировать всё, что внутри:
Ключевой момент —
---
### Извлекаем архив целиком или выборочно
Извлечь всё содержимое очень просто:
А вот выборочное извлечение:
---
### Проверяем содержимое без распаковки
Иногда нужно просто «подглядеть» внутрь ZIP:
Так можно быстро оценить структуру и размер архива.
---
zipfile для создания и извлечения архивовКогда файлов становится много, а отправить нужно «всё и сразу», на сцену выходит модуль
zipfile. Он входит в стандартную библиотеку Python, так что ничего устанавливать не нужно.---
### Создаем ZIP‑архив из одного файла
Минимальный пример:
import zipfile
with zipfile.ZipFile("data.zip", mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
zf.write("report.txt", arcname="report_in_zip.txt")
Что здесь происходит:
-
mode="w" — создаем новый архив (старый будет перезаписан).-
compression=ZIP_DEFLATED — обычное сжатие ZIP.-
write("report.txt", arcname=...) — первое имя — исходный файл, второе — как он будет называться внутри архива.---
### Добавляем несколько файлов и папку
Чтобы пройтись по директории и заархивировать всё, что внутри:
import zipfile
import os
def zip_folder(folder_path, zip_name):
with zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED) as zf:
for root, dirs, files in os.walk(folder_path):
for file_name in files:
full_path = os.path.join(root, file_name)
rel_path = os.path.relpath(full_path, folder_path)
zf.write(full_path, arcname=rel_path)
zip_folder("project", "project_backup.zip")
Ключевой момент —
rel_path. Благодаря относительному пути структура папок в архиве сохраняется аккуратно, без длинных системных путей.---
### Извлекаем архив целиком или выборочно
Извлечь всё содержимое очень просто:
import zipfile
with zipfile.ZipFile("project_backup.zip", "r") as zf:
zf.extractall("restored_project")
А вот выборочное извлечение:
import zipfile
with zipfile.ZipFile("project_backup.zip", "r") as zf:
for name in zf.namelist():
if name.endswith(".py"):
zf.extract(name, "only_scripts")
namelist() возвращает список файлов внутри архива. Можно фильтровать по расширению, имени и т.д.---
### Проверяем содержимое без распаковки
Иногда нужно просто «подглядеть» внутрь ZIP:
import zipfile
with zipfile.ZipFile("project_backup.zip", "r") as zf:
for info in zf.infolist():
print(info.filename, info.file_size, "bytes")
Так можно быстро оценить структуру и размер архива.
---
zipfile полезен для бэкапов, упаковки логов, раздачи учебных проектов и автоматизации рутины. Освоив эти несколько приёмов, вы легко встроите архивирование прямо в свои скрипты.👍2
Python для начинающих: приручаем командную строку с argparse
Рано или поздно любой скрипт вырастает из стадии «запустить и забыть» и ему хочется передавать параметры: путь к файлу, режим работы, уровень детализации логов. Жестко прописывать это в коде неудобно – каждый раз править и перезапускать. Тут и появляется герой дня — модуль
### Базовый пример: скрипт-калькулятор
Сделаем простой калькулятор, который принимает числа и операцию из командной строки:
Теперь можно запускать так:
Позиционные аргументы (
### Флаги и логические переключатели
Частая задача — включить «болтливый» режим:
Флаг без значения, просто включается при наличии в командной строке.
### Значения по умолчанию и типы
Попробуйте передать строку вместо числа — будет аккуратное сообщение с подсказкой по использованию.
### Подкоманды: один скрипт — много режимов
Когда скрипт начинает «разрастаться», удобно завести подкоманды, как у
Запуск:
Рано или поздно любой скрипт вырастает из стадии «запустить и забыть» и ему хочется передавать параметры: путь к файлу, режим работы, уровень детализации логов. Жестко прописывать это в коде неудобно – каждый раз править и перезапускать. Тут и появляется герой дня — модуль
argparse.### Базовый пример: скрипт-калькулятор
Сделаем простой калькулятор, который принимает числа и операцию из командной строки:
import argparse
def main():
parser = argparse.ArgumentParser(
description="Simple CLI calculator"
)
parser.add_argument("x", type=float, help="First number")
parser.add_argument("y", type=float, help="Second number")
parser.add_argument(
"--op", "-o",
choices=["add", "sub", "mul", "div"],
default="add",
help="Operation to perform"
)
args = parser.parse_args()
if args.op == "add":
result = args.x + args.y
elif args.op == "sub":
result = args.x - args.y
elif args.op == "mul":
result = args.x * args.y
elif args.op == "div":
result = args.x / args.y
print(result)
if __name__ == "__main__":
main()
Теперь можно запускать так:
python calc.py 10 5 --op mul
# 50.0
Позиционные аргументы (
x, y) обязательны, опция --op имеет значение по умолчанию и подсказку.### Флаги и логические переключатели
Частая задача — включить «болтливый» режим:
import argparse
parser = argparse.ArgumentParser(description="Demo for flags")
parser.add_argument("--verbose", "-v", action="store_true",
help="Enable verbose mode")
args = parser.parse_args()
if args.verbose:
print("Verbose mode is ON")
Флаг без значения, просто включается при наличии в командной строке.
### Значения по умолчанию и типы
argparse сам преобразует типы и выдаст внятную ошибку, если пользователь введет ерунду:parser.add_argument(
"--limit",
type=int,
default=10,
help="Max number of items (default: 10)"
)
Попробуйте передать строку вместо числа — будет аккуратное сообщение с подсказкой по использованию.
### Подкоманды: один скрипт — много режимов
Когда скрипт начинает «разрастаться», удобно завести подкоманды, как у
git (git add, git commit):import argparse
parser = argparse.ArgumentParser(description="User manager")
subparsers = parser.add_subparsers(dest="command", required=True)
add_parser = subparsers.add_parser("add", help="Add new user")
add_parser.add_argument("name")
add_parser.add_argument("--admin", action="store_true")
list_parser = subparsers.add_parser("list", help="List users")
args = parser.parse_args()
if args.command == "add":
print(f"Adding user {args.name}, admin={args.admin}")
elif args.command == "list":
print("Listing users...")
Запуск:
python usertool.py add Alice --admin
python usertool.py list
argparse берет на себя всю грязную работу по разбору аргументов, проверке типов и красивой справке. А вы концентрируетесь на логике программы, а не на парсинге строк.👍2
Как использовать
Если вам кажется, что для анализа данных нужны сложные библиотеки,
---
### Что такое
---
### Анализ текста: частота слов и букв
Классический пример — быстрый анализ текста.
Метод
---
### Подсчёт категорий: простой пример «аналитики»
Допустим, у вас есть список заказов с типами товаров:
Без циклов и лишнего кода вы уже понимаете распределение по категориям.
---
### Операции над
Так можно быстро сравнивать периоды, версии данных или сегменты пользователей.
---
### Фильтрация и преобразование результата
---
collections.Counter для простого анализа данныхЕсли вам кажется, что для анализа данных нужны сложные библиотеки,
Counter из модуля collections готов поспорить. Это маленький, но очень полезный инструмент, который превращает обычные списки и строки в наглядную статистику.---
### Что такое
CounterCounter — это специальный словарь для подсчёта количества элементов. Он считает, сколько раз встречается каждый объект, и делает это очень удобно.from collections import Counter
data = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counter = Counter(data)
print(counter) # Counter({'apple': 3, 'banana': 2, 'orange': 1})
print(counter['apple']) # 3
Counter сразу выдаёт вам частоты элементов. Можно думать о нём как об «умном» словаре для статистики.---
### Анализ текста: частота слов и букв
Классический пример — быстрый анализ текста.
from collections import Counter
text = "data analysis with python and data tools"
words = text.split()
word_freq = Counter(words)
char_freq = Counter(text.replace(" ", ""))
print(word_freq.most_common(3)) # топ-3 самых частых слов
print(char_freq.most_common(5)) # топ-5 самых частых символов
Метод
.most_common(n) возвращает n самых популярных элементов — готовая мини-аналитика.---
### Подсчёт категорий: простой пример «аналитики»
Допустим, у вас есть список заказов с типами товаров:
from collections import Counter
orders = [
"book", "book", "laptop", "phone",
"book", "phone", "tablet", "laptop"
]
category_stats = Counter(orders)
print(category_stats) # сколько каких товаров заказали
print(category_stats.most_common(1)) # самый популярный товар
Без циклов и лишнего кода вы уже понимаете распределение по категориям.
---
### Операции над
Counter: как с многомерными счётчикамиCounter умеет складывать, вычитать и объединять данные — удобно для сравнения наборов.from collections import Counter
week1 = Counter(['book', 'book', 'phone'])
week2 = Counter(['book', 'tablet', 'phone', 'phone'])
print(week1 + week2) # суммарные продажи
print(week2 - week1) # что «добавилось» во второй неделе
Так можно быстро сравнивать периоды, версии данных или сегменты пользователей.
---
### Фильтрация и преобразование результата
Counter легко превратить в обычный словарь и отфильтровать по условию:from collections import Counter
data = ['a', 'b', 'a', 'c', 'b', 'a', 'd']
c = Counter(data)
freq = {k: v for k, v in c.items() if v > 1}
print(freq) # {'a': 3, 'b': 2}
---
collections.Counter — отличный инструмент, чтобы «понюхать данные»: быстро понять, что в них чаще всего встречается, найти лидеров и выбросы. И всё это в пару строк кода, без тяжёлых библиотек и сложной математики.👍3
Создание 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