Python для начинающих
1.24K subscribers
546 photos
3 videos
232 files
74 links
Python для начинающих
Download Telegram
Работа с аудиофайлами: основное введение в модуль wave
Работа с аудиофайлами: основное введение в модуль wave

Представьте, что вы хотите прочитать или обработать аудиофайл формата WAV в Python. Где-то на жестком диске томно покоится классическая мелодия “Moonlight.wav”, и вашей задачей становится: узнать её параметры или скопировать пару секунд звука в новый файл. Как это сделать? Представляю простое, но очень полезное оружие из стандартной библиотеки — модуль wave.

### В чём суть формата WAV?

WAV — это контейнер для хранения необработанных (raw) аудиоданных, обычно с минимальным сжатием. Как правило, это PCM-звук: каждая секунда — это тысячи отсчётов-чисел, определяющих амплитуду сигнала.

### Простое чтение WAV-файла

Модуль wave позволяет безопасно заглянуть внутрь файла и считать метаданные: количество каналов, частоту дискретизации и длину.

import wave

with wave.open('Moonlight.wav', 'rb') as audio:
n_channels = audio.getnchannels()
sample_width = audio.getsampwidth()
frame_rate = audio.getframerate()
n_frames = audio.getnframes()
duration = n_frames / frame_rate

print(f'Channels: {n_channels}')
print(f'Sample width: {sample_width} bytes')
print(f'Frame rate: {frame_rate} Hz')
print(f'Duration: {duration:.2f} seconds')


Всё, что между with ... as audio:, работает с открытым файлом: считаем параметры и даже узнаём длительность!

### Извлечение и сохранение кусочка аудио

Допустим, нужно вырезать первые 5 секунд аудио и записать их в новый WAV файл.

import wave

with wave.open('Moonlight.wav', 'rb') as original:
frame_rate = original.getframerate()
n_frames_to_copy = frame_rate * 5
frames = original.readframes(n_frames_to_copy)

with wave.open('Snippet.wav', 'wb') as snippet:
snippet.setnchannels(original.getnchannels())
snippet.setsampwidth(original.getsampwidth())
snippet.setframerate(frame_rate)
snippet.writeframes(frames)


Модуль wave работает только с несжатым PCM-звуком, но зато позволяет управлять WAV-файлами без лишних зависимостей.

### Секреты и ограничения модуля wave

wave — это абсолютный минималист. Он не умеет работать с mp3/flac и не воспроизводит звук. Но если хочется быстро проанализировать структуру WAV или сделать простейшее редактирование, — это именно ваш инструмент.

Используйте wave, если вам нужно: узнать, как устроена “под капотом” музыка, или написать свою первую аудиопрограмму на Python!
1👍1
- Как организовать модульную структуру проекта Python с помощью init.py.
- Как организовать модульную структуру проекта Python с помощью init.py.
Python для начинающих: как приручить __init__.py и навести порядок в проекте

Когда скрипт разрастается до 500+ строк, появляется соблазн «просто дописать еще чуть-чуть». А потом — боль: сложно найти нужную функцию, тестировать, переиспользовать код. Время знакомиться с модульной структурой и файлом __init__.py.

---

### Что такое пакет и зачем нужен __init__.py

Пакет — это просто папка с Python-кодом, которую интерпретатор воспринимает как модуль. Классический пакет содержит:

my_app/
__init__.py
models.py
services.py
utils/
__init__.py
validators.py
formatters.py


Наличие __init__.py говорит Python: «это пакет, его можно импортировать».

---

### Простейший пример: группируем логику

Пусть у нас мини-приложение для работы с пользователями.

my_app/models.py:

class User:
def __init__(self, username: str):
self.username = username


my_app/services.py:

from .models import User

def create_user(username: str) -> User:
return User(username=username)


Теперь сделаем пакет удобным с помощью __init__.py.

my_app/__init__.py:

from .models import User
from .services import create_user

__all__ = ["User", "create_user"]


Теперь в основном скрипте:

from my_app import User, create_user

user = create_user("alice")
print(user.username)


Мы спрятали внутреннюю структуру (файлы models.py, services.py), оставив аккуратный публичный интерфейс.

---

### Вложенные пакеты и точечный импорт

Добавим утилиты:

my_app/utils/validators.py:

def is_valid_username(username: str) -> bool:
return len(username) >= 3


my_app/utils/__init__.py:

from .validators import is_valid_username

__all__ = ["is_valid_username"]


И обновим my_app/__init__.py:

from .models import User
from .services import create_user
from .utils import is_valid_username

__all__ = ["User", "create_user", "is_valid_username"]


Теперь в любом месте проекта:

from my_app import is_valid_username

print(is_valid_username("ab")) # False
print(is_valid_username("alex")) # True


---

### Зачем вообще заморачиваться с __init__.py

1. Чистый публичный API
Через __all__ вы явно решаете, что можно импортировать снаружи, а что считается «внутренней кухней».

2. Сокрытие структуры
Можно переименовать файл services.py в logic.py, не ломая внешний код — если __init__.py сохраняет те же имена.

3. Удобные групповые импорты
Вместо:

   from my_app.models import User
from my_app.services import create_user


достаточно:

   from my_app import User, create_user


4. Инициализация пакета
В __init__.py можно разместить код, который выполнится при первом импорте пакета: настройка логгера, загрузка конфигурации (без фанатизма).

---

### Маленькое правило напоследок

Если модулей становится слишком много, и __init__.py превращается в свалку импортов — это сигнал, что пакет пора логически разбивать дальше. Хорошая структура проекта — та, где содержимое видно «с первого взгляда», а __init__.py помогает, а не запутывает.
👍1
- Создание базового TCP-сервера с использованием сокетов Python.
Создание базового TCP‑сервера с использованием сокетов Python

Иногда хочется почувствовать себя чуть ближе к «низам» интернета — туда, где нет Flask и Django, а есть только чистые сокеты и байты. Давайте напишем свой минимальный TCP‑сервер и разберёмся, что вообще происходит «под капотом».

### Что такое сокет?

Сокет — это конечная точка сетевого соединения. У простого TCP-сервера есть три основных шага:

1. Создать сокет.
2. Привязать его к адресу и порту.
3. Слушать входящие подключения и обрабатывать клиентов.

Используем стандартный модуль socket.

### Самый простой TCP‑сервер

Напишем сервер, который принимает соединение и отсылает клиенту приветствие:

import socket

HOST = "127.0.0.1" # localhost
PORT = 5000 # любой свободный порт > 1024

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(1) # максимальная очередь из 1 клиента

print(f"Server is listening on {HOST}:{PORT}...")

conn, addr = server_socket.accept()
print(f"Connected by {addr}")

message = "Hello from TCP server!\n"
conn.sendall(message.encode("utf-8"))

conn.close()
server_socket.close()


Кратко по шагам:

- socket.AF_INET — IPv4;
- socket.SOCK_STREAM — протокол TCP;
- bind — говорим ОС: «Этот сервер живёт на HOST:PORT»;
- listen — включаем режим ожидания подключений;
- accept — блокируется, пока клиент не подключится, и возвращает новый сокет conn только для этого клиента.

Проверить можно через telnet 127.0.0.1 5000 или написать маленького клиента.

### Эхо‑сервер: отвечаем на сообщения

Сделаем сервер, который читает данные от клиента и отправляет их обратно:

import socket

HOST = "127.0.0.1"
PORT = 5001

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Echo server on {HOST}:{PORT}")

while True:
conn, addr = server_socket.accept()
print(f"Client connected: {addr}")
with conn:
while True:
data = conn.recv(1024) # читаем до 1024 байт
if not data:
break # клиент закрыл соединение
print(f"Received: {data!r}")
conn.sendall(data) # отправляем назад


Здесь важно:

- recv возвращает bytes; пустые b"" означают разрыв соединения;
- sendall гарантирует отправку всего буфера;
- цикл while True позволяет обслуживать клиентов один за другим.

### О чём стоит помнить

- Порт должен быть свободен, иначе будет ошибка OSError: [Errno 98] Address already in use.
- При разработке удобно использовать 127.0.0.1, для внешнего доступа — "0.0.0.0".
- Такой сервер однопоточен: пока он общается с одним клиентом, другие ждут. Для реальных задач нужны потоки (threading) или asyncio.

Через такие простые примеры становится понятнее, что веб‑фреймворки всего лишь надстройка над таким же socket, который вы сейчас написали сами.
👍3
- Как настроить воркфлоу автоматизации на GitHub Actions для Python-проекта.
Как настроить GitHub Actions для Python-проекта: CI за 10 минут

Ты пишешь код, пушишь его в репозиторий… и только потом вспоминаешь, что не запустил тесты. Классика. GitHub Actions решает эту проблему: он запускает проверки автоматически при каждом коммите или pull request.

Разберём минимальный, но полезный воркфлоу для Python.

---

### Шаг 1. Структура проекта

Допустим, у тебя такой проект:

.
├─ src/
│ └─ main.py
├─ tests/
│ └─ test_main.py
├─ requirements.txt
└─ pyproject.toml (или setup.cfg / setup.py — не критично)


tests/ — место для тестов, requirements.txt — зависимости.

---

### Шаг 2. Пишем простой код и тест

src/main.py:

def add(a: int, b: int) -> int:
return a + b


tests/test_main.py:

from src.main import add


def test_add_positive():
assert add(2, 3) == 5


def test_add_negative():
assert add(-1, -3) == -4


Добавь pytest в requirements.txt:

pytest==8.3.0


---

### Шаг 3. Создаём воркфлоу GitHub Actions

В репозитории создай папку .github/workflows/ и файл, например python-ci.yml.

name: Python CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
tests:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ["3.10", "3.11"]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Run tests
run: pytest -q


Что тут происходит:

- on — когда запускать воркфлоу: при push и pull_request в main.
- matrix — прогоняем тесты на нескольких версиях Python.
- actions/checkout — забирает код из репозитория.
- actions/setup-python — ставит нужную версию Python.
- Install dependencies — ставим зависимости.
- Run tests — запускаем pytest.

---

### Шаг 4. Бонус: проверка стиля

Добавим линтер flake8:

requirements.txt:

pytest==8.3.0
flake8==7.1.0


И ещё один шаг в воркфлоу:

      - name: Lint with flake8
run: |
flake8 src tests


Теперь каждый коммит проходит:

1. Установку зависимостей
2. Линтер
3. Тесты на нескольких версиях Python

Если что-то падает — GitHub подсветит это прямо в Pull Request.

---

GitHub Actions превращает твой репозиторий в маленький конвейер качества: ты пишешь код — он сам проверяет, что ты ничего не сломал. Настроить один раз — экономить часы и нервы постоянно.
👍2
- Введение в matplotlib: базовые графики и настройка их стилей.
Введение в matplotlib: базовые графики и стильные фишки

Matplotlib — это как карандаш для программиста: без него работать можно, но рисовать данные неудобно. Библиотека умеет строить почти любые графики, а начинать лучше с простого: линейных, точечных и столбчатых диаграмм.

---

### Первый график за 5 строк

Установите библиотеку (если нужно):

pip install matplotlib


Базовый пример:

import matplotlib.pyplot as plt

x = [1, 2, 3, 4]
y = [2, 3, 5, 7]

plt.plot(x, y)
plt.title("Simple line plot")
plt.xlabel("X axis")
plt.ylabel("Y axis")
plt.show()


plot — линия, title — заголовок, xlabel/ylabel — подписи осей, show — вывод окна с графиком.

---

### Цвета, маркеры и стили линий

Matplotlib позволяет легко “приодеть” график:

import matplotlib.pyplot as plt

x = [1, 2, 3, 4]
y1 = [1, 4, 9, 16]
y2 = [2, 3, 2, 3]

plt.plot(x, y1, color="red", linestyle="--", marker="o", label="squares")
plt.plot(x, y2, color="#008080", linestyle="-.", marker="s", label="wavy")

plt.title("Styled lines")
plt.xlabel("X")
plt.ylabel("Y")
plt.legend()
plt.grid(True)
plt.show()


Что полезно запомнить:
- color: "red", "green", "black" или HEX "#008080".
- linestyle: "-", "--", "-.", ":".
- marker: "o", "s", "x", "^" и др.
- legend() — показывает легенду по label.

---

### Точечные и столбчатые графики

Для разброса точек используйте scatter:

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [5, 3, 4, 2, 1]

plt.scatter(x, y, color="purple", s=80, alpha=0.7)
plt.title("Scatter plot")
plt.xlabel("X")
plt.ylabel("Y")
plt.grid(True)
plt.show()


Параметры: s — размер точек, alpha — прозрачность.

Столбчатая диаграмма:

import matplotlib.pyplot as plt

labels = ["Mon", "Tue", "Wed", "Thu", "Fri"]
values = [10, 12, 9, 15, 11]

plt.bar(labels, values, color="orange")
plt.title("Weekly stats")
plt.ylabel("Value")
plt.show()


---

### Быстрая смена стиля оформления

Matplotlib поддерживает готовые стили:

import matplotlib.pyplot as plt

plt.style.use("ggplot") # попробуйте также "seaborn-v0_8", "dark_background"

x = [1, 2, 3, 4]
y = [3, 8, 1, 10]

plt.plot(x, y, marker="o")
plt.title("Styled with ggplot")
plt.show()


Один вызов plt.style.use — и у вас другой фон, сетка и палитра.

---

Matplotlib хорош тем, что простой код уже даёт читабельные графики, а добавляя по одному параметру — цвет, легенду, стиль — вы постепенно превращаете “сырой” график в понятную визуализацию.
👍3
- Как использовать модуль argparse для разбор параметров CLI.
Как подружиться с argparse и сделать свой первый CLI-инструмент

Почти каждый полезный скрипт рано или поздно превращается в маленькую консольную утилиту: нужно передать путь к файлу, режим работы, уровень логирования. Пихать всё в input() — путь страданий. Для этого в стандартной библиотеке есть модуль argparse, который превращает ваш скрипт в удобный CLI-инструмент с автогенерируемой справкой.

---

### Базовый пример: обязательный аргумент

Скрипт, который приветствует пользователя по имени:

import argparse

parser = argparse.ArgumentParser(description="Simple greeting script")
parser.add_argument("name", help="User name to greet")

args = parser.parse_args()
print(f"Hello, {args.name}!")


Запускаем из терминала:

python greet.py Alice
# Hello, Alice!


Попробуйте python greet.py -h — справка генерируется автоматически.

---

### Опциональные флаги и значения по умолчанию

Добавим флаг --uppercase и параметр --times:

import argparse

parser = argparse.ArgumentParser(description="Advanced greeting script")
parser.add_argument("name", help="User name to greet")
parser.add_argument(
"-t", "--times",
type=int,
default=1,
help="How many times to repeat greeting"
)
parser.add_argument(
"-u", "--uppercase",
action="store_true",
help="Print greeting in uppercase"
)

args = parser.parse_args()

greeting = f"Hello, {args.name}!"
if args.uppercase:
greeting = greeting.upper()

for _ in range(args.times):
print(greeting)


Примеры запуска:

python greet.py Bob -t 3
python greet.py Bob -t 2 -u


---

### Выбор из ограниченного набора значений

Частая задача — режим работы: debug, info, error. Используем choices:

import argparse

parser = argparse.ArgumentParser(description="Logging level demo")
parser.add_argument(
"--level",
choices=["debug", "info", "error"],
default="info",
help="Logging level"
)

args = parser.parse_args()
print(f"Selected level: {args.level}")


Если передать неправильное значение, argparse сам ругнется и покажет помощь.

---

### Что в итоге

argparse умеет:

- разбирать позиционные и опциональные аргументы;
- автоматически генерировать -h/--help;
- проверять типы (type=int, float, и т.д.);
- ограничивать значения (choices);
- удобно работать с флагами (action="store_true").

Освоив этот модуль, вы переводите свои скрипты из категории «сделал для себя» в категорию «это можно отдавать другим и не стыдиться».
👍1
- Работа с Google Sheets API для автоматизации работы с таблицами.
Python для начинающих: автоматизируем Google Sheets через API

Работа с Excel надоела, а таблицы в Google Sheets растут как грибы? Самое время подключить Python и заставить таблицы работать за вас.

### Что понадобится

1. Аккаунт Google.
2. Включить Google Sheets API и создать Service Account в Google Cloud Console.
3. Скачать JSON‑ключ сервисного аккаунта.
4. Выдать этому сервисному аккаунту доступ к нужной таблице (Share → по email из JSON).

Устанавливаем нужные пакеты:

pip install gspread google-auth


### Подключение к таблице

import gspread
from google.oauth2.service_account import Credentials

SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
creds = Credentials.from_service_account_file(
"service_account.json",
scopes=SCOPES
)

client = gspread.authorize(creds)
sheet = client.open("Sales Report").sheet1 # первая вкладка


Теперь sheet — это объект рабочей таблицы, с которым можно делать почти всё.

### Чтение данных

Получим все строки и заголовок:

rows = sheet.get_all_records()  # список dict'ов
header = sheet.row_values(1) # первая строка
print(header)
print(rows[:3]) # первые три записи


Так удобно превращать таблицу в «мини-базу данных».

### Запись и обновление

Запишем заголовок и пару строк:

data_header = ["Date", "Product", "Quantity", "Price"]
sheet.update("A1:D1", [data_header])

new_rows = [
["2025-01-01", "Keyboard", 3, 59.9],
["2025-01-02", "Mouse", 5, 29.5],
]
sheet.append_rows(new_rows)


append_rows добавляет данные в конец, не нужно считать, какая строка следующая.

Обновим цену в конкретной ячейке:

sheet.update("D2", 79.9)


### Массовые обновления

Если нужно поменять сразу блок данных — используем диапазон:

discounted = [
["Keyboard", 49.9],
["Mouse", 24.9],
]
sheet.update("B2:C3", discounted)


### Небольшая автоматизация: перерасчёт итогов

Добавим в таблицу столбец с итоговой суммой:

values = sheet.get_all_values()
header = values[0]
rows = values[1:]

quantity_idx = header.index("Quantity")
price_idx = header.index("Price")

totals = []
for row in rows:
try:
qty = float(row[quantity_idx])
price = float(row[price_idx])
totals.append([qty * price])
except ValueError:
totals.append([""])

start_row = 2
end_row = start_row + len(totals) - 1
sheet.update(f"E{start_row}:E{end_row}", totals)
sheet.update("E1", "Total")


Теперь таблица сама считает сумму по строке, а вы можете раз в день запускать скрипт и обновлять отчеты.

Google Sheets API + Python — это быстрый способ превратить обычную таблицу в часть автоматизированного пайплайна: собирать данные, очищать, пересчитывать и готовить отчеты без ручного копипаста.
🔥31
- Как работать с параллельными потоками данных с использованием Apache Kafka.
Как работать с параллельными потоками данных с Apache Kafka и Python

Представь себе конвейер в заводском цеху: по ленте бесконечно едут детали, а рабочие на разных станциях что‑то с ними делают. Apache Kafka — это примерно такой же конвейер, только для данных. Она позволяет принимать, хранить и отдавать миллионы сообщений в реальном времени. Python здесь может быть “рабочим”, который эти данные обрабатывает.

---

### Основные термины Kafka

- Broker — сервер Kafka, который хранит сообщения.
- Topic — “лента”, куда пишутся сообщения (например, user_events).
- Partition — кусок топика. Именно разделы позволяют обрабатывать сообщения параллельно.
- Producer — отправляет сообщения в Kafka.
- Consumer — читает сообщения из Kafka.
- Consumer group — группа потребителей, которые совместно читают один топик, автоматически деля партиции между собой.

Чем больше партиций у топика и чем больше потребителей в группе, тем выше параллелизм.

---

### Установка клиента для Python

Один из популярных клиентов — confluent-kafka (тонкая обертка над C-библиотекой):

pip install confluent-kafka


---

### Простой producer: отправляем события

from confluent_kafka import Producer
import json

producer = Producer({"bootstrap.servers": "localhost:9092"})

def delivery_report(err, msg):
if err is not None:
print(f"Delivery failed: {err}")
else:
print(f"Delivered to {msg.topic()} [{msg.partition()}] at offset {msg.offset()}")

for user_id in range(1, 6):
event = {"user_id": user_id, "action": "click"}
producer.produce(
topic="user_events",
value=json.dumps(event).encode("utf-8"),
callback=delivery_report
)

producer.flush()


Сообщения попадают в топик user_events. Kafka сама распределяет их по партициям (по умолчанию — по хешу ключа, если он есть).

---

### Параллельное чтение: несколько consumers в одной группе

Ключ к параллельности — использовать одну consumer group и задать топику несколько партиций (например, 3). Тогда, если ты запустишь этот скрипт в нескольких процессах, Kafka раздаст им партиции автоматически.

from confluent_kafka import Consumer, KafkaException
import json

consumer = Consumer({
"bootstrap.servers": "localhost:9092",
"group.id": "analytics_group",
"auto.offset.reset": "earliest",
})

consumer.subscribe(["user_events"])

try:
while True:
msg = consumer.poll(1.0)
if msg is None:
continue
if msg.error():
raise KafkaException(msg.error())
event = json.loads(msg.value().decode("utf-8"))
print(
f"Process: handled user={event['user_id']} "
f"from partition={msg.partition()} offset={msg.offset()}"
)
finally:
consumer.close()


Теперь магия:
- Увеличиваешь количество процессов/контейнеров с этим consumer’ом — обработка масштабируется.
- Kafka сама следит, кто какие партиции читает, и перераспределяет их при падении одного из экземпляров.

---

### Практические советы

1. Думай о ключах сообщений: одинаковый ключ → одинаковая партиция → удобно гарантировать порядок для конкретного пользователя.
2. Используй consumer groups для разных задач: аналитика, логирование, алерты могут читать один и тот же топик независимо.
3. Следи за offset’ами: по умолчанию Kafka фиксирует прогресс чтения, чтобы после рестартов не терять сообщения.

Kafka + Python позволяют строить конвейеры обработки данных, которые легко масштабируются горизонтально. Для начинающего питониста это хороший шаг от “скриптиков” к полноценным потоковым системам.
👍1
- Основы работы с мемкэшем и Redis для кэширования данных.
Основы работы с Memcached и Redis для кэширования данных

Если ваш Python‑код постоянно лезет в базу за одними и теми же данными — вы жжёте процессор и тормозите приложение. В таких случаях на сцену выходят Memcached и Redis — быстрые in‑memory хранилища, идеальные для кэширования.

---

### Зачем нужен кэш

Кэширование — это сохранение заранее вычисленных результатов в память, чтобы не пересчитывать и не запрашивать их повторно. Типичный сценарий:

1. Проверяем кэш.
2. Если данных нет — обращаемся к БД / API.
3. Кладём результат в кэш.
4. В следующий раз берём из кэша за миллисекунды.

---

### Memcached: минималистичный и быстрый

Memcached — это просто распределённый словарь в памяти: ключ–значение, без сложных типов данных.

Установка клиента:

pip install pymemcache


Пример:

from pymemcache.client import base

client = base.Client(("localhost", 11211))

def get_user_profile(user_id: int) -> dict:
cache_key = f"user:{user_id}"
cached = client.get(cache_key)
if cached:
# данные хранятся как bytes; в реальном коде — json.loads(...)
print("from cache")
return eval(cached.decode("utf-8"))

print("from db")
user = {"id": user_id, "name": "Alice"} # тут должна быть реальная БД
client.set(cache_key, str(user).encode("utf-8"), expire=60)
return user

get_user_profile(1)
get_user_profile(1)


Особенности Memcached:
- Нет персистентности: перезапустили — всё забыто.
- Простые типы (строки/байты).
- Идеален как быстрый временный кэш поверх БД.

---

### Redis: швейцарский нож кэширования

Redis умеет больше: строки, списки, множества, хэши, TTL, pub/sub, транзакции и персистентность.

Установка:

pip install redis


Пример кэша с TTL:

import json
from redis import Redis

redis_client = Redis(host="localhost", port=6379, db=0)

def get_product(product_id: int) -> dict:
cache_key = f"product:{product_id}"
cached = redis_client.get(cache_key)
if cached:
print("from cache")
return json.loads(cached)

print("from db")
product = {"id": product_id, "title": "Keyboard", "price": 99.9}
redis_client.set(cache_key, json.dumps(product), ex=120)
return product

get_product(10)
get_product(10)


Пример использования сложных структур: счётчик просмотров товара:

def increment_views(product_id: int) -> int:
key = f"product:{product_id}:views"
return redis_client.incr(key)

print(increment_views(10))
print(increment_views(10))


---

### Что выбрать новичку

- Memcached, если нужен простой, сверхбыстрый кэш без сложных структур и персистентности.
- Redis, если хотите кэш + дополнительные возможности (счётчики, очереди, сессии, персистентность).

Для учебных проектов Redis часто удобнее: он универсален и его легче масштабировать по мере усложнения системы.
👍2
- Использование pandas для преобразования данных и очистки наборов данных.
Использование pandas для преобразования и очистки данных

Если вы хоть раз открывали «сырой» CSV-файл, то знаете: данные редко бывают аккуратными. Пропуски, дубли, странные форматы дат — всё это мешает анализу. Здесь на сцену выходит pandas — швейцарский нож для работы с табличными данными в Python.

---

### Загрузка и первый взгляд на данные

Начнём с простого: прочитаем CSV и посмотрим, что внутри.

import pandas as pd

df = pd.read_csv("sales.csv")
print(df.head())
print(df.info())


head() показывает первые строки, info() — типы столбцов и наличие пропусков. Уже отсюда часто можно понять, где бардак: например, даты хранятся как object, а суммы — как строки.

---

### Преобразование типов и работа с датами

Частая проблема — строки там, где должны быть числа или даты.

df["amount"] = pd.to_numeric(df["amount"], errors="coerce")
df["date"] = pd.to_datetime(df["date"], errors="coerce")


Параметр errors="coerce" превращает невозможные значения в NaN. Это удобно: дальше их можно явно обработать, а не ловить таинственные ошибки.

---

### Очистка пропусков и дубликатов

Пропуски можно либо удалить, либо заполнить осмысленными значениями.

# Удаляем строки, где нет суммы покупки
df = df.dropna(subset=["amount"])

# Заполняем пропущенный город значением "Unknown"
df["city"] = df["city"].fillna("Unknown")

# Удаляем полные дубликаты строк
df = df.drop_duplicates()


Для числовых столбцов часто используют среднее, медиану или 0 — зависит от задачи:

df["discount"] = df["discount"].fillna(df["discount"].median())


---

### Создание новых признаков

Pandas позволяет легко добавлять столбцы, основанные на уже существующих.

df["year"] = df["date"].dt.year
df["month"] = df["date"].dt.month
df["amount_with_tax"] = df["amount"] * 1.2


За пару строк кода можно превратить «сырые» данные в удобный набор признаков для анализа или модели.

---

### Фильтрация и группировка

Комбинация фильтрации и группировки — основа исследовательского анализа.

# Оставим только заказы после 2023 года и с положительной суммой
filtered = df[(df["date"].dt.year >= 2023) & (df["amount"] > 0)]

# Посчитаем суммарные продажи по городам
city_stats = (
filtered
.groupby("city", as_index=False)["amount"]
.sum()
.rename(columns={"amount": "total_amount"})
)

print(city_stats.head())


С помощью groupby и агрегирующих функций (sum, mean, count и др.) можно быстро получать сводки и отчёты, не трогая Excel.

---

pandas превращает хаотичный CSV в аккуратный, структурированный датафрейм, с которым приятно работать. Чем раньше вы начнёте его использовать, тем меньше времени будете тратить на рутину и тем больше — на сам анализ.
👍1