### Python для начинающих: создаем красивый прогресс-бар с
Когда скрипт «думает» дольше пары секунд, пользователь начинает волноваться: «Он вообще работает или уже завис?». Прогресс-бар в терминале решает эту проблему — и модуль
---
## Установка
---
## Базовый пример
Минимальный прогресс-бар для цикла:
Что происходит:
-
- выводит прогресс, процент, скорость и время до окончания.
---
## Прогресс-бар для списков и генераторов
Аргумент
---
## Ручное обновление прогресса
Если у вас нет удобного итератора (например, цикл внутри функции), можно обновлять прогресс вручную:
Здесь:
-
-
---
## Обёртка для функций:
Если вы обрабатываете коллекцию с помощью
Важный момент: для
---
## Быстрый чек-лист по
- Подходит для любых итерируемых объектов.
- Умеет считать время, скорость и ETA.
- Можно использовать как контекстный менеджер и обновлять вручную.
- Практически не требует изменений логики программы.
Попробуйте добавить
tqdmКогда скрипт «думает» дольше пары секунд, пользователь начинает волноваться: «Он вообще работает или уже завис?». Прогресс-бар в терминале решает эту проблему — и модуль
tqdm делает это буквально одной строкой кода.---
## Установка
tqdm не входит в стандартную библиотеку, его нужно установить:pip install tqdm
---
## Базовый пример
Минимальный прогресс-бар для цикла:
from time import sleep
from tqdm import tqdm
for i in tqdm(range(100)):
sleep(0.05) # имитация работы
Что происходит:
-
tqdm(iterable) оборачивает любой итерируемый объект (range, список, генератор);- выводит прогресс, процент, скорость и время до окончания.
---
## Прогресс-бар для списков и генераторов
tqdm не требует именно range. Можно использовать списки:from time import sleep
from tqdm import tqdm
files = ["data1.csv", "data2.csv", "data3.csv"]
for filename in tqdm(files, desc="Processing"):
sleep(0.5) # обработка файла
Аргумент
desc добавляет понятное описание слева от прогресс-бара.---
## Ручное обновление прогресса
Если у вас нет удобного итератора (например, цикл внутри функции), можно обновлять прогресс вручную:
from time import sleep
from tqdm import tqdm
total_steps = 5
with tqdm(total=total_steps, desc="Custom task") as pbar:
for step in range(total_steps):
sleep(0.7) # тяжелая операция
pbar.update(1)
Здесь:
-
total — общее количество шагов;-
pbar.update(n) — сообщаем, что выполнено n шагов.---
## Обёртка для функций:
tqdm + mapЕсли вы обрабатываете коллекцию с помощью
map, можно добавить прогресс почти без изменений кода:from time import sleep
from tqdm import tqdm
def process_item(item):
sleep(0.2)
return item * 2
items = list(range(50))
results = list(tqdm(map(process_item, items), total=len(items)))
Важный момент: для
map нужно явно указать total=len(items), иначе tqdm не знает, сколько всего элементов.---
## Быстрый чек-лист по
tqdm- Подходит для любых итерируемых объектов.
- Умеет считать время, скорость и ETA.
- Можно использовать как контекстный менеджер и обновлять вручную.
- Практически не требует изменений логики программы.
Попробуйте добавить
tqdm в ваш текущий скрипт с долгими циклами — и терминал сразу станет дружелюбнее для пользователя… и для вас.👍4🔥1
Изучение модуля
Когда речь заходит о безопасности и уникальной идентификации данных в Python, почти всегда всплывает модуль
### Что такое хеш?
Хеш-функция берет любые данные (строку, файл, пароль) и превращает их в строку фиксированной длины. Малейшее изменение исходных данных кардинально меняет хеш — это удобно для проверки целостности.
### Первый пример: хеш строки
Попробуйте поменять хотя бы один символ в
### Проверка целостности данных
Допустим, вы скачали файл и хотите убедиться, что он не поврежден. Принцип тот же — считаем хеш и сравниваем.
Здесь мы читаем файл по частям, чтобы не загружать его целиком в память (важно для больших файлов).
### Хеш пароля (но по‑простому)
Подчеркиваю: это упрощенный подход для понимания, а не готовое решение для продакшена.
Зачем
### Быстрый обзор алгоритмов
Самые используемые:
Для новых проектов
---
hashlib: простое хеширование данныхКогда речь заходит о безопасности и уникальной идентификации данных в Python, почти всегда всплывает модуль
hashlib. Он не шифрует данные (это важно!), а превращает их в «отпечаток» — фиксированную строку, по которой нельзя восстановить оригинал, но можно проверить, не изменился ли он.### Что такое хеш?
Хеш-функция берет любые данные (строку, файл, пароль) и превращает их в строку фиксированной длины. Малейшее изменение исходных данных кардинально меняет хеш — это удобно для проверки целостности.
### Первый пример: хеш строки
import hashlib
text = "Hello, hashlib!"
text_bytes = text.encode("utf-8") # хешируем всегда байты
hash_object = hashlib.sha256(text_bytes)
hash_hex = hash_object.hexdigest()
print(hash_hex)
sha256 — один из популярных алгоритмов. Метод .hexdigest() возвращает хеш в виде удобной шестнадцатеричной строки.Попробуйте поменять хотя бы один символ в
text — длина хеша останется той же, но значение полностью изменится.### Проверка целостности данных
Допустим, вы скачали файл и хотите убедиться, что он не поврежден. Принцип тот же — считаем хеш и сравниваем.
import hashlib
def file_hash(path, algorithm="sha256", chunk_size=8192):
hash_obj = getattr(hashlib, algorithm)()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(chunk_size), b""):
hash_obj.update(chunk)
return hash_obj.hexdigest()
print(file_hash("example.txt"))
Здесь мы читаем файл по частям, чтобы не загружать его целиком в память (важно для больших файлов).
### Хеш пароля (но по‑простому)
Подчеркиваю: это упрощенный подход для понимания, а не готовое решение для продакшена.
import hashlib
def hash_password(password: str, salt: str) -> str:
data = (password + salt).encode("utf-8")
return hashlib.sha256(data).hexdigest()
password_hash = hash_password("my_secret_password", "random_salt_123")
print(password_hash)
Зачем
salt? Без него одинаковые пароли у разных пользователей давали бы одинаковые хеши. Соль делает результат уникальным.### Быстрый обзор алгоритмов
import hashlib
print(hashlib.algorithms_guaranteed)
Самые используемые:
md5, sha1, sha224, sha256, sha384, sha512. Для новых проектов
md5 и sha1 уже считаются слабыми для безопасности, но всё ещё полезны для простой проверки целостности (там, где нет угрозы взлома).---
hashlib — отличный инструмент, чтобы аккуратно и предсказуемо «упаковывать» данные в компактные отпечатки. Понимание хеширования — обязательный шаг на пути к более продвинутым темам: аутентификация, токены, работа с файлами и безопасностью в целом.🔥4👍1
### Работа с zip-архивами с помощью стандартного
Zip-архивы — это такой «контейнер» для файлов: удобно отправить, сохранить, логировать бэкапы. В Python для этого ничего устанавливать не нужно — в стандартной библиотеке уже есть модуль
---
## Создаем zip-архив
Начнем с базового: упакуем несколько файлов в один архив.
Ключевые моменты:
-
-
-
---
## Добавляем файлы в существующий архив
Иногда нужно дописать архив, не пересоздавая его:
Режим
---
## Просмотр содержимого архива
Посмотрим, что внутри, без распаковки:
Объект
---
## Распаковка архива
Распакуем либо всё, либо один конкретный файл:
---
## Чтение файла прямо из архива
Иногда распаковывать на диск не хочется — удобнее прочитать файл «на лету»:
Так можно, например, читать настройки, ресурсы или шаблоны прямо из архива.
---
zipfileZip-архивы — это такой «контейнер» для файлов: удобно отправить, сохранить, логировать бэкапы. В Python для этого ничего устанавливать не нужно — в стандартной библиотеке уже есть модуль
zipfile.---
## Создаем zip-архив
Начнем с базового: упакуем несколько файлов в один архив.
import zipfile
from pathlib import Path
def create_archive(archive_name, files):
with zipfile.ZipFile(archive_name, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
for file_path in files:
file_path = Path(file_path)
zf.write(file_path, arcname=file_path.name)
create_archive("project.zip", ["main.py", "config.json", "README.md"])
Ключевые моменты:
-
mode="w" — создать новый архив (старый с таким именем будет перезаписан).-
compression=ZIP_DEFLATED — сжатие, без него файлы просто «складываются» в контейнер.-
arcname — имя файла внутри архива (можно отличать от исходного пути).---
## Добавляем файлы в существующий архив
Иногда нужно дописать архив, не пересоздавая его:
import zipfile
def append_to_archive(archive_name, file_path):
with zipfile.ZipFile(archive_name, mode="a", compression=zipfile.ZIP_DEFLATED) as zf:
zf.write(file_path)
append_to_archive("project.zip", "log.txt")
Режим
a — «append», дозапись.---
## Просмотр содержимого архива
Посмотрим, что внутри, без распаковки:
import zipfile
def list_archive(archive_name):
with zipfile.ZipFile(archive_name, mode="r") as zf:
for info in zf.infolist():
print(f"{info.filename:20} {info.file_size:8} bytes")
list_archive("project.zip")
Объект
ZipInfo содержит размер, дату изменения, атрибуты и т.д.---
## Распаковка архива
Распакуем либо всё, либо один конкретный файл:
import zipfile
def extract_all(archive_name, target_dir):
with zipfile.ZipFile(archive_name, mode="r") as zf:
zf.extractall(path=target_dir)
def extract_file(archive_name, member, target_dir):
with zipfile.ZipFile(archive_name, mode="r") as zf:
zf.extract(member, path=target_dir)
extract_all("project.zip", "unpacked/")
extract_file("project.zip", "config.json", "configs/")
---
## Чтение файла прямо из архива
Иногда распаковывать на диск не хочется — удобнее прочитать файл «на лету»:
import zipfile
def read_text_from_zip(archive_name, member):
with zipfile.ZipFile(archive_name, mode="r") as zf:
with zf.open(member) as f:
return f.read().decode("utf-8")
content = read_text_from_zip("project.zip", "README.md")
print(content)
Так можно, например, читать настройки, ресурсы или шаблоны прямо из архива.
---
zipfile покрывает 90% бытовых задач с архивами: упаковка, дописывание, просмотр, извлечение и чтение «на лету». Всё это — без сторонних библиотек и с парой строк кода.👍6
Как использовать модуль
Иногда Python‑кода мало. Нужно дернуть системную утилиту: запустить
---
### Простой запуск и получение вывода
Самый удобный вход — функция
Ключевые моменты:
- Команда передаётся как список:
-
-
---
### Обработка ошибок
Если вы хотите, чтобы Python падал при ошибке команды, используйте
---
### Передача данных на stdin
Иногда внешней программе нужно что‑то «скормить» через стандартный ввод:
Параметр
---
### Непосредственное взаимодействие:
Если нужно более тонкое управление (долгоживущий процесс, чтение вывода построчно и т.п.), используйте
Здесь процесс живет «параллельно», а вы можете читать его вывод по мере поступления.
---
### Кратко по безопасности
- Не передавайте в
- Предпочитайте список аргументов вместо одной строки.
- Не включайте
---
subprocess для запуска внешних командИногда Python‑кода мало. Нужно дернуть системную утилиту: запустить
ping, запустить другой скрипт, конвертировать видео через ffmpeg. Для этого есть модуль subprocess — безопасная и гибкая альтернатива старым os.system.---
### Простой запуск и получение вывода
Самый удобный вход — функция
subprocess.run. Она запускает команду, ждет завершения и возвращает объект с результатом:import subprocess
result = subprocess.run(
["echo", "Hello from subprocess"],
capture_output=True,
text=True
)
print("Return code:", result.returncode)
print("Stdout:", result.stdout)
print("Stderr:", result.stderr)
Ключевые моменты:
- Команда передаётся как список:
["echo", "text"], а не одной строкой. Это безопаснее и избавляет от проблем с кавычками.-
capture_output=True собирает stdout и stderr.-
text=True декодирует байты в строки (по умолчанию UTF-8).---
### Обработка ошибок
Если вы хотите, чтобы Python падал при ошибке команды, используйте
check=True:import subprocess
try:
subprocess.run(
["ls", "/definitely_not_exists"],
check=True,
capture_output=True,
text=True
)
except subprocess.CalledProcessError as exc:
print("Command failed!")
print("Return code:", exc.returncode)
print("Stderr:", exc.stderr)
CalledProcessError содержит всю полезную информацию о провалившейся команде.---
### Передача данных на stdin
Иногда внешней программе нужно что‑то «скормить» через стандартный ввод:
import subprocess
text_to_send = "line 1\nline 2\n"
result = subprocess.run(
["grep", "line"],
input=text_to_send,
text=True,
capture_output=True
)
print(result.stdout)
Параметр
input= передает данные в stdin процесса.---
### Непосредственное взаимодействие:
PopenЕсли нужно более тонкое управление (долгоживущий процесс, чтение вывода построчно и т.п.), используйте
Popen:import subprocess
proc = subprocess.Popen(
["ping", "-c", "3", "python.org"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
for line in proc.stdout:
print("PING OUTPUT:", line.strip())
return_code = proc.wait()
print("Return code:", return_code)
Здесь процесс живет «параллельно», а вы можете читать его вывод по мере поступления.
---
### Кратко по безопасности
- Не передавайте в
subprocess строки, собранные из непроверенного пользовательского ввода.- Предпочитайте список аргументов вместо одной строки.
- Не включайте
shell=True, если это не абсолютно необходимо. Именно shell=True чаще всего открывает дверь инъекциям команд.---
subprocess — это мост между вашим Python‑кодом и всем остальным миром командной строки. Освоив его, вы сможете автоматизировать практически любую системную задачу, не покидая Python.👍5
Python для начинающих: структурированные логи без боли и магии
Обычный
---
### Простейший логгер с форматом
Модуль
Теперь вместо разрозненных сообщений у вас есть чёткая структура: время, уровень, имя логгера, текст.
---
### Логи, которые можно парсить: JSON-формат
Когда логов много, их хочется складывать в системы вроде ELK или Loki. Для этого удобно писать их в JSON.
Минимальный пример без сторонних библиотек:
Обрати внимание:
---
### Контекст: больше данных, меньше шума
Ещё одна фишка —
Теперь все логи в рамках запроса будут помечены одним
---
Структурированные логи — это не «красивее вывод», а фундамент для нормальной отладки и мониторинга. Настроив форматы однажды, вы перестанете бояться смотреть на логи крупных проектов.
Обычный
print() в продакшене — как дневник на стикерах: пока записей мало, всё понятно, но стоит коду вырасти — и начинается хаос. Здесь на сцену выходит модуль logging и особенно — форматирование логов.---
### Простейший логгер с форматом
Модуль
logging уже в стандартной библиотеке. Начнем с базовой настройки:import logging
logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter(
fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("Application started")
logger.warning("Low disk space")
Теперь вместо разрозненных сообщений у вас есть чёткая структура: время, уровень, имя логгера, текст.
---
### Логи, которые можно парсить: JSON-формат
Когда логов много, их хочется складывать в системы вроде ELK или Loki. Для этого удобно писать их в JSON.
Минимальный пример без сторонних библиотек:
import logging
import json
import sys
from datetime import datetime
class JsonFormatter(logging.Formatter):
def format(self, record):
log_record = {
"time": datetime.fromtimestamp(record.created).isoformat(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
if record.exc_info:
log_record["exception"] = self.formatException(record.exc_info)
return json.dumps(log_record, ensure_ascii=False)
logger = logging.getLogger("json_app")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)
logger.info("User logged in", extra={"user_id": 42})
Обрати внимание:
extra по умолчанию не попадёт в log_record. Чтобы добавлять свои поля, можно расширить JsonFormatter и пройтись по record.__dict__, отфильтровав служебные ключи.---
### Контекст: больше данных, меньше шума
Ещё одна фишка —
LoggerAdapter. Он позволяет приклеить к логам контекст (например, request_id):import logging
base_logger = logging.getLogger("service")
base_logger.setLevel(logging.INFO)
base_handler = logging.StreamHandler()
base_handler.setFormatter(logging.Formatter("%(levelname)s | %(request_id)s | %(message)s"))
base_logger.addHandler(base_handler)
class RequestAdapter(logging.LoggerAdapter):
def process(self, msg, kwargs):
kwargs.setdefault("extra", {})
kwargs["extra"].setdefault("request_id", self.extra.get("request_id", "-"))
return msg, kwargs
logger = RequestAdapter(base_logger, {"request_id": "abc-123"})
logger.info("Processing started")
Теперь все логи в рамках запроса будут помечены одним
request_id — дебагить станет гораздо проще.---
Структурированные логи — это не «красивее вывод», а фундамент для нормальной отладки и мониторинга. Настроив форматы однажды, вы перестанете бояться смотреть на логи крупных проектов.
👍5
Как написать простой чат-сервер с использованием
Асинхронность в Python звучит пугающе, но именно она позволяет одному скрипту одновременно обслуживать десятки клиентов. Отличный способ это почувствовать — написать свой мини-чат-сервер на
### Идея чата
У нас будет:
- сервер, который принимает подключения;
- список активных клиентов;
- функция, рассылающая сообщения всем подключённым.
Используем высокоуровневый API:
### Базовый сервер
### Как это запустить и протестировать
1. Запускаете сервер:
2. В другом терминале подключаетесь через
-
или
-
3. Откройте 2–3 таких клиента и начните печатать сообщения. Всё, что отправляет один клиент, увидят остальные.
### Что важно понять
-
-
-
Дальше можно добавить ники, команды (
asyncioАсинхронность в Python звучит пугающе, но именно она позволяет одному скрипту одновременно обслуживать десятки клиентов. Отличный способ это почувствовать — написать свой мини-чат-сервер на
asyncio.### Идея чата
У нас будет:
- сервер, который принимает подключения;
- список активных клиентов;
- функция, рассылающая сообщения всем подключённым.
Используем высокоуровневый API:
asyncio.start_server, StreamReader и StreamWriter.### Базовый сервер
import asyncio
clients = set()
async def handle_client(reader: asyncio.StreamReader,
writer: asyncio.StreamWriter):
addr = writer.get_extra_info("peername")
clients.add(writer)
print(f"Client connected: {addr}")
try:
while data := await reader.readline():
message = data.decode().rstrip()
if not message:
continue
full_msg = f"{addr}: {message}\n"
await broadcast(full_msg, sender=writer)
except asyncio.IncompleteReadError:
pass
finally:
print(f"Client disconnected: {addr}")
clients.remove(writer)
writer.close()
await writer.wait_closed()
async def broadcast(message: str, sender: asyncio.StreamWriter | None = None):
dead = []
for client in clients:
if client is sender:
continue
try:
client.write(message.encode())
await client.drain()
except ConnectionError:
dead.add(client)
for client in dead:
clients.discard(client)
async def main():
server = await asyncio.start_server(handle_client, "127.0.0.1", 8888)
addr = ", ".join(str(sock.getsockname()) for sock in server.sockets)
print(f"Server started at {addr}")
async with server:
await server.serve_forever()
if __name__ == "__main__":
asyncio.run(main())
### Как это запустить и протестировать
1. Запускаете сервер:
python chat_server.py.2. В другом терминале подключаетесь через
telnet или nc:-
telnet 127.0.0.1 8888 или
-
nc 127.0.0.1 88883. Откройте 2–3 таких клиента и начните печатать сообщения. Всё, что отправляет один клиент, увидят остальные.
### Что важно понять
-
async def и await позволяют не блокировать сервер во время ожидания ввода.-
start_server создаёт корутину, которая для каждого клиента запускает handle_client.-
broadcast показывает ключевую идею: один сервер — много клиентов, минимум блокировок.Дальше можно добавить ники, команды (
/quit, /users), простую авторизацию — всё это поверх уже готового асинхронного фундамента.👍5
Изучение шаблонов проектирования на простых примерах: Singleton, Factory
В какой‑то момент любой код на Python вырастает до такого размера, что простых функций и классов уже мало. Хочется порядка. Тут в игру выходят шаблоны проектирования. Разберём два базовых — Singleton и Factory — на минимальных, но жизненных примерах.
---
## Singleton: когда нужен только один экземпляр
Singleton гарантирует, что у класса будет всего один объект. Полезно для настроек приложения, логгера, подключения к БД.
### Пример: глобальные настройки приложения
---
## Factory: когда нужно создавать разные объекты одинаковым способом
Factory инкапсулирует логику создания объектов. Внешнему коду не важно, какой именно класс используется — он просто просит «сделай мне подходящий объект».
### Пример: парсер данных в разных форматах
Теперь, если появится
---
Singleton помогает централизовать состояние, а Factory — спрятать логику создания объектов за единым интерфейсом. Оба шаблона отлично тренируют умение думать о структуре программы, а не только о том, «как бы это запустить, чтобы не упало».
В какой‑то момент любой код на Python вырастает до такого размера, что простых функций и классов уже мало. Хочется порядка. Тут в игру выходят шаблоны проектирования. Разберём два базовых — Singleton и Factory — на минимальных, но жизненных примерах.
---
## Singleton: когда нужен только один экземпляр
Singleton гарантирует, что у класса будет всего один объект. Полезно для настроек приложения, логгера, подключения к БД.
### Пример: глобальные настройки приложения
class AppConfig:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self, debug=False):
if self._initialized:
return
self.debug = debug
self._initialized = True
cfg1 = AppConfig(debug=True)
cfg2 = AppConfig(debug=False)
print(cfg1 is cfg2) # True
print(cfg1.debug, cfg2.debug) # True True
__new__ контролирует создание объекта и хранит единственный экземпляр в _instance. __init__ запускается при каждом вызове класса, поэтому мы защищаемся флагом _initialized, чтобы не перезаписывать настройки.---
## Factory: когда нужно создавать разные объекты одинаковым способом
Factory инкапсулирует логику создания объектов. Внешнему коду не важно, какой именно класс используется — он просто просит «сделай мне подходящий объект».
### Пример: парсер данных в разных форматах
class JsonParser:
def parse(self, data: str):
return {"type": "json", "raw": data}
class XmlParser:
def parse(self, data: str):
return {"type": "xml", "raw": data}
class ParserFactory:
@staticmethod
def get_parser(format_name: str):
format_name = format_name.lower()
if format_name == "json":
return JsonParser()
if format_name == "xml":
return XmlParser()
raise ValueError(f"Unknown format: {format_name}")
parser = ParserFactory.get_parser("json")
result = parser.parse('{"id": 1}')
print(result)
Теперь, если появится
YamlParser, мы просто добавим его в ParserFactory, не переписывая весь остальной код.---
Singleton помогает централизовать состояние, а Factory — спрятать логику создания объектов за единым интерфейсом. Оба шаблона отлично тренируют умение думать о структуре программы, а не только о том, «как бы это запустить, чтобы не упало».
👍3❤1
Модуль
Когда данные растут, как снежный ком, возникает вопрос: «И что со всем этим делать?»
В Python для этого есть модуль
---
### Среднее:
Среднее арифметическое — это классическое "в среднем по больнице".
---
### Медиана:
Медиана — "середина" отсортированных данных. Она куда устойчивее к выбросам.
Для четного количества значений можно управлять поведением:
---
### Дисперсия и стандартное отклонение: насколько данные "разбросаны"
Если среднее говорит "где центр", то дисперсия и стандартное отклонение — "насколько всё вокруг центра разлетается".
Чем больше дисперсия и стандартное отклонение, тем сильнее отличаются значения внутри набора.
Важно:
---
### Небольшой практический пример
Оценим стабильность времени отклика сервиса:
Если среднее сильно больше медианы и стандартное отклонение велико — у вас есть редкие, но тяжелые "подвисания", которые нельзя игнорировать.
---
statistics: среднее, медиана и дисперсия без боли в головеКогда данные растут, как снежный ком, возникает вопрос: «И что со всем этим делать?»
В Python для этого есть модуль
statistics — маленький, но очень полезный набор инструментов для анализа чисел.---
### Среднее:
meanСреднее арифметическое — это классическое "в среднем по больнице".
from statistics import mean
temperatures = [18, 20, 21, 19, 22, 20]
avg_temp = mean(temperatures)
print(avg_temp) # 20.0
mean() чувствителен к выбросам. Если добавить одно странное значение, картина исказится:from statistics import mean
salaries = [50_000, 55_000, 52_000, 60_000, 1_000_000]
print(mean(salaries)) # 243000.0 — выглядит уже не так реалистично
---
### Медиана:
median и median_low / median_highМедиана — "середина" отсортированных данных. Она куда устойчивее к выбросам.
from statistics import median
salaries = [50_000, 55_000, 52_000, 60_000, 1_000_000]
print(median(salaries)) # 55_000 — намного ближе к реальности
Для четного количества значений можно управлять поведением:
from statistics import median_low, median_high
values = [1, 2, 100, 101]
print(median_low(values)) # 2
print(median_high(values)) # 100
---
### Дисперсия и стандартное отклонение: насколько данные "разбросаны"
Если среднее говорит "где центр", то дисперсия и стандартное отклонение — "насколько всё вокруг центра разлетается".
from statistics import variance, stdev
scores = [70, 72, 71, 69, 70, 71]
print(variance(scores)) # маленькое значение — все близко к среднему
print(stdev(scores)) # стандартное отклонение
Чем больше дисперсия и стандартное отклонение, тем сильнее отличаются значения внутри набора.
Важно:
variance() и stdev() требуют минимум 2 значения. Для всей генеральной совокупности есть pvariance() и pstdev() — используют немного другие формулы:from statistics import pvariance, pstdev
scores = [70, 72, 71, 69, 70, 71]
print(pvariance(scores))
print(pstdev(scores))
---
### Небольшой практический пример
Оценим стабильность времени отклика сервиса:
from statistics import mean, median, stdev
response_times = [110, 120, 115, 300, 118, 117, 119]
print("mean:", mean(response_times))
print("median:", median(response_times))
print("stdev:", stdev(response_times))
Если среднее сильно больше медианы и стандартное отклонение велико — у вас есть редкие, но тяжелые "подвисания", которые нельзя игнорировать.
---
statistics — идеальный вход в мир анализа данных: минимум кода, максимум информации о ваших числах. Начните с этих функций, и набор цифр перестанет быть просто хаосом.❤3👍2🔥2
Как использовать watchdog для отслеживания изменений в файлах
Иногда хочется, чтобы скрипт сам реагировал на изменения в файлах: перезапускал сборку, обновлял данные, копировал новый файл в бэкап. Переписывать бесконечные циклы
---
### Установка
---
### Простейший пример: реагируем на изменения в папке
Создадим наблюдателя, который будет печатать, когда что-то происходит в директории:
Теперь, если в папке создать/изменить/удалить файл — скрипт отреагирует.
---
### Фильтрация по типу файлов
Часто нужно отслеживать только, скажем,
Так можно запускать, например, линтер или тесты только при изменении исходников.
---
### Автоматическая реакция: мини‑автосборка
Пример: при изменении файла конфигурации перегенерировать выходной файл.
Теперь любое сохранение
---
Иногда хочется, чтобы скрипт сам реагировал на изменения в файлах: перезапускал сборку, обновлял данные, копировал новый файл в бэкап. Переписывать бесконечные циклы
while True с time.sleep — скучно и неэффективно. Для этого есть отличный модуль — watchdog.---
### Установка
pip install watchdog
watchdog работает кроссплатформенно и умеет «слушать» файловую систему почти как системные утилиты.---
### Простейший пример: реагируем на изменения в папке
Создадим наблюдателя, который будет печатать, когда что-то происходит в директории:
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import time
from pathlib import Path
class SimpleHandler(FileSystemEventHandler):
def on_modified(self, event):
print(f"Modified: {event.src_path}")
def on_created(self, event):
print(f"Created: {event.src_path}")
def on_deleted(self, event):
print(f"Deleted: {event.src_path}")
if __name__ == "__main__":
path = Path(".").resolve()
event_handler = SimpleHandler()
observer = Observer()
observer.schedule(event_handler, str(path), recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
Теперь, если в папке создать/изменить/удалить файл — скрипт отреагирует.
---
### Фильтрация по типу файлов
Часто нужно отслеживать только, скажем,
.py или .txt. Добавим простую проверку:class PyFilesHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.is_directory:
return
if not event.src_path.endswith(".py"):
return
print(f"Python file changed: {event.src_path}")
Так можно запускать, например, линтер или тесты только при изменении исходников.
---
### Автоматическая реакция: мини‑автосборка
Пример: при изменении файла конфигурации перегенерировать выходной файл.
import subprocess
class BuildHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.is_directory:
return
if event.src_path.endswith("config.yaml"):
print("Config changed, rebuilding...")
subprocess.run(["python", "build.py"])
Теперь любое сохранение
config.yaml автоматически запускает build.py.---
watchdog — мощный инструмент для автоматизации рутинных задач вокруг файловой системы: автосборка проектов, синхронизация папок, логирование изменений. Главное — один раз написать обработчик событий, а дальше файлы будут «говорить» с вашим кодом сами.🔥5