Основы проверки кода с использованием pylint и flake8
Когда начинаешь писать на Python, код обычно «работает — и ладно». Но довольно быстро становится ясно: читать его через неделю невозможно даже самому автору. Тут на сцену выходят линтеры — инструменты автоматической проверки стиля и потенциальных ошибок. Два самых популярных:
---
### Зачем вообще нужны линтеры?
Линтеры помогают:
- находить опечатки и потенциальные баги (неиспользуемые переменные, лишние импорты);
- поддерживать единый стиль кода (отступы, длина строк, имена переменных);
- учиться писать «питоничный» код — по PEP 8 и здравому смыслу.
---
### Установка
Проверка файла:
---
### Простой пример «плохого» кода
Типичные проблемы, которые найдут линтеры:
- несколько импортов в одной строке (
- несогласованные отступы;
- отсутствие пробелов вокруг операторов;
- вызов
---
### Как это исправить
Теперь
---
### В чём разница между pylint и flake8?
- flake8 — лёгкий и быстрый. Фокусируется на стиле и базовых ошибках.
- pylint — гораздо более строгий: анализирует структуру проекта, связи между модулями, даёт «оценку» кода, умеет ловить более сложные проблемы.
Часто их используют вместе:
---
### Настройка под себя
Оба инструмента можно «приручить» конфигами:
-
-
Например, увеличить допустимую длину строки:
---
Лучший момент начать использовать линтеры — сейчас. Они быстро превращают хаотичный учебный код в аккуратный и читаемый, а заодно приучают к хорошему стилю, почти как строгий, но полезный наставник.
Когда начинаешь писать на Python, код обычно «работает — и ладно». Но довольно быстро становится ясно: читать его через неделю невозможно даже самому автору. Тут на сцену выходят линтеры — инструменты автоматической проверки стиля и потенциальных ошибок. Два самых популярных:
pylint и flake8.---
### Зачем вообще нужны линтеры?
Линтеры помогают:
- находить опечатки и потенциальные баги (неиспользуемые переменные, лишние импорты);
- поддерживать единый стиль кода (отступы, длина строк, имена переменных);
- учиться писать «питоничный» код — по PEP 8 и здравому смыслу.
---
### Установка
pip install pylint flake8
Проверка файла:
pylint my_script.py
flake8 my_script.py
---
### Простой пример «плохого» кода
# file: bad_example.py
import os, sys
def add(a,b):
return a+ b
def main():
result = add(2,2)
print("Result:",result)
main()
Типичные проблемы, которые найдут линтеры:
- несколько импортов в одной строке (
import os, sys);- несогласованные отступы;
- отсутствие пробелов вокруг операторов;
- вызов
main() без проверки if __name__ == "__main__":.---
### Как это исправить
# file: good_example.py
import os
import sys
def add(a: int, b: int) -> int:
return a + b
def main() -> None:
result = add(2, 2)
print("Result:", result)
if __name__ == "__main__":
main()
Теперь
flake8 в основном будет молчать, а pylint может ещё подсказать, что os и sys нигде не используются.---
### В чём разница между pylint и flake8?
- flake8 — лёгкий и быстрый. Фокусируется на стиле и базовых ошибках.
- pylint — гораздо более строгий: анализирует структуру проекта, связи между модулями, даёт «оценку» кода, умеет ловить более сложные проблемы.
Часто их используют вместе:
flake8 — для стилистики, pylint — для более глубокой проверки.---
### Настройка под себя
Оба инструмента можно «приручить» конфигами:
-
pylint: файл .pylintrc-
flake8: файл .flake8 или раздел [flake8] в setup.cfgНапример, увеличить допустимую длину строки:
# .flake8
[flake8]
max-line-length = 100
---
Лучший момент начать использовать линтеры — сейчас. Они быстро превращают хаотичный учебный код в аккуратный и читаемый, а заодно приучают к хорошему стилю, почти как строгий, но полезный наставник.
👍2
Как организовать модульную структуру проекта в Python
Когда скрипт на 50 строк превращается в файл на 500, наступает момент, когда хочется просто… закрыть редактор. Тут и приходит на помощь модульная структура: разбиваем хаос на аккуратные части, которые легко читать, тестировать и переиспользовать.
---
### Что такое модуль и пакет?
- Модуль — обычный
- Пакет — папка с Python-модулями. Обычно содержит файл
Простейшая структура проекта может выглядеть так:
Но уже на среднем проекте удобнее перейти к пакетам:
---
### Пример: мини-приложение с модулями
Представим, что пишем упрощенную систему заказов.
Мы разделили:
- models — только данные.
- services — бизнес-логика.
- utils — вспомогательные функции.
Каждый файл отвечает за свою зону, и код перестает быть «простыней».
---
### Относительные и абсолютные импорты
Внутри пакета удобно использовать относительные импорты:
А снаружи — абсолютные:
Это делает структуру понятной: глядя на импорт, сразу видно, откуда что приходит.
---
### Зачем все это?
- Проще тестировать: можно импортировать один модуль и тестировать его изолированно.
- Легче находить код: логика не размазана по одному файлу.
- Удобно расширять проект: добавили новый модуль — остальное не пострадало.
Модульная структура — это не «красиво», это «жить можно», когда код растет. Начать лучше сразу, даже если проект пока умещается в один файл.
Когда скрипт на 50 строк превращается в файл на 500, наступает момент, когда хочется просто… закрыть редактор. Тут и приходит на помощь модульная структура: разбиваем хаос на аккуратные части, которые легко читать, тестировать и переиспользовать.
---
### Что такое модуль и пакет?
- Модуль — обычный
.py-файл. Например: utils.py.- Пакет — папка с Python-модулями. Обычно содержит файл
__init__.py (в современных версиях Python он не обязателен, но лучше его иметь для явности).Простейшая структура проекта может выглядеть так:
my_project/
main.py
models.py
utils.py
Но уже на среднем проекте удобнее перейти к пакетам:
my_project/
main.py
app/
__init__.py
models.py
services.py
utils.py
---
### Пример: мини-приложение с модулями
Представим, что пишем упрощенную систему заказов.
app/models.py:from dataclasses import dataclass
@dataclass
class Order:
id: int
amount: float
is_paid: bool = False
app/services.py:from .models import Order
from .utils import apply_discount
def process_order(order: Order, discount: float) -> Order:
order.amount = apply_discount(order.amount, discount)
order.is_paid = True
return order
app/utils.py:def apply_discount(amount: float, discount: float) -> float:
if not 0 <= discount <= 1:
raise ValueError("Invalid discount")
return round(amount * (1 - discount), 2)
main.py:from app.models import Order
from app.services import process_order
def main():
order = Order(id=1, amount=100.0)
order = process_order(order, discount=0.15)
print(order)
if __name__ == "__main__":
main()
Мы разделили:
- models — только данные.
- services — бизнес-логика.
- utils — вспомогательные функции.
Каждый файл отвечает за свою зону, и код перестает быть «простыней».
---
### Относительные и абсолютные импорты
Внутри пакета удобно использовать относительные импорты:
from .models import Order
from .utils import apply_discount
А снаружи — абсолютные:
from app.services import process_order
Это делает структуру понятной: глядя на импорт, сразу видно, откуда что приходит.
---
### Зачем все это?
- Проще тестировать: можно импортировать один модуль и тестировать его изолированно.
- Легче находить код: логика не размазана по одному файлу.
- Удобно расширять проект: добавили новый модуль — остальное не пострадало.
Модульная структура — это не «красиво», это «жить можно», когда код растет. Начать лучше сразу, даже если проект пока умещается в один файл.
🔥2👍1
Создание собственных исключений для понятной обработки ошибок
Стандартные исключения Python (
---
## Зачем нужны свои исключения
Представьте интернет‑магазин. Ошибка “ValueError” может означать всё что угодно: от некорректной цены до пустого списка товаров. Гораздо понятнее:
-
-
-
Код сразу становится самодокументируемым: по имени исключения видно, что пошло не так и где это обрабатывать.
---
## Как создать своё исключение
Создание простое: наследуемся от
Используем:
---
## Иерархия исключений
Красота начинается, когда вы строите семейство ошибок:
Теперь можно:
Преимущество: вы можете ловить как конкретные, так и все “магазинные” ошибки разом.
---
## Добавляем контекст к исключениям
Исключения могут не только “орать”, но и нести данные:
Использование:
Так обработчик ошибок видит не только текст, но и структурированные данные.
---
## Когда точно стоит заводить своё исключение
- Ошибка связана с предметной областью (деньги, заказы, уровни в игре).
- Вам нужно по‑разному реагировать на разные типы ошибок.
- Вы пишете код, которым будут пользоваться другие разработчики.
Если же ошибка — просто “не тот тип” или “не то значение”, часто достаточно стандартных исключений.
Собственные исключения — это не “косметика”, а мощный инструмент архитектуры. Они превращают хаос “что-то упало” в чёткую систему сигналов, по которой программа может разумно реагировать на проблемы.
Стандартные исключения Python (
ValueError, TypeError, KeyError и т.д.) хороши, но иногда они слишком общие. Если вы пишете библиотеку, игру или веб‑сервис, вам нужно чётко понимать: какая именно логическая ошибка произошла и что с ней делать. Тут и вступают в игру собственные исключения.---
## Зачем нужны свои исключения
Представьте интернет‑магазин. Ошибка “ValueError” может означать всё что угодно: от некорректной цены до пустого списка товаров. Гораздо понятнее:
-
InvalidPriceError-
OutOfStockError-
PaymentDeclinedErrorКод сразу становится самодокументируемым: по имени исключения видно, что пошло не так и где это обрабатывать.
---
## Как создать своё исключение
Создание простое: наследуемся от
Exception (или другого базового класса исключений):class InvalidPriceError(Exception):
"""Raised when product price is invalid."""
pass
Используем:
def set_price(price: float) -> None:
if price <= 0:
raise InvalidPriceError(f"Price must be positive, got: {price}")
---
## Иерархия исключений
Красота начинается, когда вы строите семейство ошибок:
class ShopError(Exception):
"""Base exception for shop-related errors."""
pass
class OutOfStockError(ShopError):
pass
class PaymentError(ShopError):
pass
class PaymentDeclinedError(PaymentError):
pass
Теперь можно:
try:
process_order(order)
except PaymentDeclinedError as e:
log_warning(e)
show_message("Payment was declined, try another card.")
except ShopError as e:
log_error(e)
show_message("Something went wrong with your order.")
Преимущество: вы можете ловить как конкретные, так и все “магазинные” ошибки разом.
---
## Добавляем контекст к исключениям
Исключения могут не только “орать”, но и нести данные:
class OutOfStockError(ShopError):
def __init__(self, product_id: int, requested: int, available: int):
self.product_id = product_id
self.requested = requested
self.available = available
message = (f"Product {product_id}: requested {requested}, "
f"available {available}")
super().__init__(message)
Использование:
try:
reserve_product(product_id=42, quantity=10)
except OutOfStockError as e:
print(e.product_id, e.requested, e.available)
Так обработчик ошибок видит не только текст, но и структурированные данные.
---
## Когда точно стоит заводить своё исключение
- Ошибка связана с предметной областью (деньги, заказы, уровни в игре).
- Вам нужно по‑разному реагировать на разные типы ошибок.
- Вы пишете код, которым будут пользоваться другие разработчики.
Если же ошибка — просто “не тот тип” или “не то значение”, часто достаточно стандартных исключений.
Собственные исключения — это не “косметика”, а мощный инструмент архитектуры. Они превращают хаос “что-то упало” в чёткую систему сигналов, по которой программа может разумно реагировать на проблемы.
🔥3❤2👍1
Простое шифрование текста с базовым алгоритмом Caesar
Когда-то, задолго до хакеров и брутфорса, шифрование могло выглядеть почти игрушкой. Один из самых простых и известных методов — шифр Цезаря. Он настолько прост, что идеально подходит для первых шагов в программировании и работе со строками в Python.
Идея простая: каждую букву заменяем другой, сдвинутой в алфавите на фиксированное число позиций. Например, при сдвиге 3 буква
### Базовая реализация
Сделаем функцию, которая шифрует только латинские буквы, сохраняя регистр и игнорируя символы вне алфавита:
Функция
### Расшифровка — тот же алгоритм
Дешифровать можно тем же кодом, просто меняя знак сдвига:
Мы не пишем второй алгоритм, а переиспользуем первый — хороший пример того, как небольшая продуманная функция упрощает жизнь.
### Немного автоматизации
Добавим простую «оболочку», чтобы пользователь вводил текст и сдвиг:
Такой простой проект полезен сразу в нескольких вещах:
- работа со строками и символами;
- понимание циклов и условий;
- практика написания маленьких, переиспользуемых функций.
Шифр Цезаря давно не считается надежным, но как учебный пример — это отличный старт, чтобы почувствовать, как из простых операций над символами рождается алгоритм шифрования.
Когда-то, задолго до хакеров и брутфорса, шифрование могло выглядеть почти игрушкой. Один из самых простых и известных методов — шифр Цезаря. Он настолько прост, что идеально подходит для первых шагов в программировании и работе со строками в Python.
Идея простая: каждую букву заменяем другой, сдвинутой в алфавите на фиксированное число позиций. Например, при сдвиге 3 буква
A превращается в D, B в E и так далее по кругу.### Базовая реализация
Сделаем функцию, которая шифрует только латинские буквы, сохраняя регистр и игнорируя символы вне алфавита:
def caesar_encrypt(text, shift):
result = []
for ch in text:
if 'a' <= ch <= 'z':
base = ord('a')
offset = (ord(ch) - base + shift) % 26
result.append(chr(base + offset))
elif 'A' <= ch <= 'Z':
base = ord('A')
offset = (ord(ch) - base + shift) % 26
result.append(chr(base + offset))
else:
result.append(ch)
return ''.join(result)
message = "Hello, World!"
encrypted = caesar_encrypt(message, 3)
print(encrypted) # Khoor, Zruog!
Функция
ord превращает символ в его числовой код, chr делает обратное, а % 26 обеспечивает «цикличность» по алфавиту.### Расшифровка — тот же алгоритм
Дешифровать можно тем же кодом, просто меняя знак сдвига:
def caesar_decrypt(text, shift):
return caesar_encrypt(text, -shift)
decrypted = caesar_decrypt(encrypted, 3)
print(decrypted) # Hello, World!
Мы не пишем второй алгоритм, а переиспользуем первый — хороший пример того, как небольшая продуманная функция упрощает жизнь.
### Немного автоматизации
Добавим простую «оболочку», чтобы пользователь вводил текст и сдвиг:
def run_caesar():
text = input("Enter text: ")
shift = int(input("Enter shift (e.g. 3): "))
mode = input("Mode (e for encrypt, d for decrypt): ").strip().lower()
if mode == 'e':
print("Encrypted:", caesar_encrypt(text, shift))
elif mode == 'd':
print("Decrypted:", caesar_decrypt(text, shift))
else:
print("Unknown mode")
if __name__ == "__main__":
run_caesar()
Такой простой проект полезен сразу в нескольких вещах:
- работа со строками и символами;
- понимание циклов и условий;
- практика написания маленьких, переиспользуемых функций.
Шифр Цезаря давно не считается надежным, но как учебный пример — это отличный старт, чтобы почувствовать, как из простых операций над символами рождается алгоритм шифрования.
👍2
Создание календаря событий с помощью модуля
Модуль
---
### Базовый календарь на месяц
Начнем с простого вывода календаря:
Каждая строка — неделя, нули означают «нет дня» (пустая клетка в начале/конце месяца).
---
### Добавляем события
Сделаем простейший календарь событий: у нас есть словарь
Результат — текстовый календарь, где дни с событиями помечены
---
### Поиск событий по дате и будням
Так можно, например, подсветить события, которые попадают на выходные, или не планировать релизы на понедельник.
---
calendarМодуль
calendar — один из тех, что часто игнорируют, а зря. С его помощью можно быстро сделать простой «календарь событий» прямо в консоли или подготовить данные для бота/веб-приложения.---
### Базовый календарь на месяц
Начнем с простого вывода календаря:
import calendar
year = 2025
month = 1
cal = calendar.TextCalendar(firstweekday=0) # 0 — понедельник
print(cal.formatmonth(year, month))
TextCalendar рисует аккуратную таблицу в виде текста. Если нужен «машиночитаемый» вариант, лучше использовать monthcalendar:import calendar
year = 2025
month = 1
weeks = calendar.monthcalendar(year, month)
for week in weeks:
print(week)
Каждая строка — неделя, нули означают «нет дня» (пустая клетка в начале/конце месяца).
---
### Добавляем события
Сделаем простейший календарь событий: у нас есть словарь
events, где ключ — кортеж (year, month, day), а значение — текст события. Отметим события звездочкой:import calendar
events = {
(2025, 1, 5): "Project deadline",
(2025, 1, 12): "Team meeting",
(2025, 1, 31): "Release day",
}
def print_events_calendar(year, month):
cal = calendar.TextCalendar(firstweekday=0)
weeks = calendar.monthcalendar(year, month)
print(f"{calendar.month_name[month]} {year}".center(20))
print("Mo Tu We Th Fr Sa Su")
for week in weeks:
row = []
for day in week:
if day == 0:
row.append(" ")
continue
mark = "*" if (year, month, day) in events else " "
cell = f"{day:2d}{mark}"
# обрежем до 3 символов, чтобы выровнять сетку
row.append(cell[:3])
print(" ".join(row))
print_events_calendar(2025, 1)
Результат — текстовый календарь, где дни с событиями помечены
*. Можно менять символ на !, # и т.п.---
### Поиск событий по дате и будням
calendar знает всё о днях недели:import calendar
from datetime import date
def is_weekend(year, month, day):
weekday = date(year, month, day).weekday() # 0=Mon, 6=Sun
return weekday >= 5
for key, title in events.items():
y, m, d = key
day_name = calendar.day_name[date(y, m, d).weekday()]
tag = " (weekend)" if is_weekend(y, m, d) else ""
print(f"{y}-{m:02d}-{d:02d}: {title} — {day_name}{tag}")
Так можно, например, подсветить события, которые попадают на выходные, или не планировать релизы на понедельник.
---
calendar отлично подходит для прототипирования: быстрый текстовый календарь, базовая логика дат, дни недели, учет високосных лет — все уже есть «из коробки». Остается только обернуть это в интерфейс, который вам нужен.👍3
### Работа с временными зонами и UTC: почему
Работа со временем — один из самых подлых участков в Python-проектах. Летнее время, разные часовые пояса, сервер в UTC, пользователь в другом конце планеты — всё это легко ломает логику. Разберём, как навести порядок с помощью модуля
---
## Наивные и «осознанные» даты
Модуль
- Наивный
- Aware (
Такой объект опасен: сравнения и арифметика могут работать неверно, если смешивать его с датами из других зон.
---
## Добавляем pytz и приводим всё к UTC
Золотое правило: внутри приложения хранить время в UTC, а локальное время использовать только на входе/выходе.
Теперь
---
## Локальное время пользователя → UTC
Допустим, пользователь в Нью-Йорке выбирает время встречи:
Важно: нельзя делать
---
## UTC → локальное время пользователя
Когда храним в базе UTC, а показываем пользователю в его поясе:
---
## Типичные правила, чтобы не утонуть во времени
1. Всегда сохраняй UTC в базе.
2. На границе системы:
- входящие данные → локализуешь через
- исходящие данные → из UTC в нужную зону через
3. Не используй
4. Явно задавай
pytz до сих пор нуженРабота со временем — один из самых подлых участков в Python-проектах. Летнее время, разные часовые пояса, сервер в UTC, пользователь в другом конце планеты — всё это легко ломает логику. Разберём, как навести порядок с помощью модуля
pytz.---
## Наивные и «осознанные» даты
Модуль
datetime в Python умеет хранить даты с информацией о временной зоне и без неё.- Наивный
datetime не знает, в каком он часовом поясе.- Aware (
tz-aware) — содержит таймзону и может корректно переводиться в другие.from datetime import datetime
naive_dt = datetime.now()
print(naive_dt.tzinfo) # None — часовой пояс неизвестен
Такой объект опасен: сравнения и арифметика могут работать неверно, если смешивать его с датами из других зон.
---
## Добавляем pytz и приводим всё к UTC
Золотое правило: внутри приложения хранить время в UTC, а локальное время использовать только на входе/выходе.
from datetime import datetime
import pytz
utc = pytz.utc
utc_now = datetime.now(utc)
print(utc_now, utc_now.tzinfo) # Время в UTC
Теперь
utc_now — «осознанный» объект, и его можно безопасно конвертировать в любой другой часовой пояс.---
## Локальное время пользователя → UTC
Допустим, пользователь в Нью-Йорке выбирает время встречи:
from datetime import datetime
import pytz
user_tz = pytz.timezone("America/New_York")
# Пользователь ввёл время (например, из формы)
naive_meeting = datetime(2025, 3, 10, 15, 0, 0)
# Правильный способ "прикрутить" таймзону
local_meeting = user_tz.localize(naive_meeting)
utc_meeting = local_meeting.astimezone(pytz.utc)
print("Local:", local_meeting)
print("UTC: ", utc_meeting)
Важно: нельзя делать
naive_meeting.replace(tzinfo=user_tz) — это обходит правила перехода на летнее/зимнее время и даёт неверный результат.---
## UTC → локальное время пользователя
Когда храним в базе UTC, а показываем пользователю в его поясе:
import pytz
# Время из БД в UTC
stored_utc = utc_meeting # из предыдущего примера
user_tz = pytz.timezone("Asia/Tokyo")
user_time = stored_utc.astimezone(user_tz)
print("For user in Tokyo:", user_time)
pytz учитывает исторические изменения, летнее время и прочие «сюрпризы» календарей.---
## Типичные правила, чтобы не утонуть во времени
1. Всегда сохраняй UTC в базе.
2. На границе системы:
- входящие данные → локализуешь через
timezone.localize(...) → переводишь в UTC;- исходящие данные → из UTC в нужную зону через
astimezone(...).3. Не используй
replace(tzinfo=...) для смены зоны.4. Явно задавай
tzinfo, не полагайся на наивные datetime.now().pytz выглядит простым, но помогает избежать очень дорогих ошибок во времени, особенно когда пользователи живут в разных точках мира.👍2
Преобразование текста в речь с помощью pyttsx3: заставляем Python говорить
Иногда куда удобнее услышать результат работы программы, чем читать его в консоли. Например, при создании голосового помощника, озвучивании уведомлений или чтении текстов вслух. Для этого в Python есть отличный модуль —
---
## Установка
---
## Первый голос программы
Минимальный пример:
Разбор по шагам:
-
-
-
---
## Управление скоростью и громкостью
Сделаем речь быстрее и тише:
Полезно, если вы хотите, чтобы уведомления говорились короче и быстрее, а чтение длинных текстов — медленнее и отчётливее.
---
## Выбор голоса
На системе может быть несколько голосов (мужской, женский, разные языки). Посмотрим, что доступно:
Выбор голоса по индексу:
Если у вас установлены дополнительные русские/английские голоса, их можно выбрать по
---
## Сохранение речи в аудиофайл
Озвучку можно не только проигрывать сразу, но и сохранять в файл, например, чтобы сделать аудиоверсию текста:
Теперь
---
Иногда куда удобнее услышать результат работы программы, чем читать его в консоли. Например, при создании голосового помощника, озвучивании уведомлений или чтении текстов вслух. Для этого в Python есть отличный модуль —
pyttsx3.pyttsx3 — офлайн‑движок синтеза речи: интернет не нужен, библиотека использует системные голосовые движки (SAPI5 на Windows, NSSpeechSynthesizer на macOS, eSpeak на Linux).---
## Установка
pip install pyttsx3
---
## Первый голос программы
Минимальный пример:
import pyttsx3
engine = pyttsx3.init()
engine.say("Hello, Python world!")
engine.runAndWait()
Разбор по шагам:
-
init() создаёт объект движка;-
say() добавляет текст в очередь;-
runAndWait() запускает озвучку и ждёт её завершения.---
## Управление скоростью и громкостью
Сделаем речь быстрее и тише:
import pyttsx3
engine = pyttsx3.init()
rate = engine.getProperty("rate")
volume = engine.getProperty("volume")
engine.setProperty("rate", rate + 50) # быстрее
engine.setProperty("volume", volume - 0.2) # тише (0.0–1.0)
engine.say("This is a faster and quieter voice.")
engine.runAndWait()
Полезно, если вы хотите, чтобы уведомления говорились короче и быстрее, а чтение длинных текстов — медленнее и отчётливее.
---
## Выбор голоса
На системе может быть несколько голосов (мужской, женский, разные языки). Посмотрим, что доступно:
import pyttsx3
engine = pyttsx3.init()
voices = engine.getProperty("voices")
for idx, voice in enumerate(voices):
print(idx, voice.id)
Выбор голоса по индексу:
engine.setProperty("voice", voices[0].id)
engine.say("Using the first available voice.")
engine.runAndWait()
Если у вас установлены дополнительные русские/английские голоса, их можно выбрать по
voice.id, проверяя в выводе нужный язык.---
## Сохранение речи в аудиофайл
Озвучку можно не только проигрывать сразу, но и сохранять в файл, например, чтобы сделать аудиоверсию текста:
import pyttsx3
engine = pyttsx3.init()
engine.save_to_file("This text will be saved to an audio file.", "output.mp3")
engine.runAndWait()
Теперь
output.mp3 можно отправить, встроить в приложение или проиграть через любой плеер.---
pyttsx3 — отличный старт для тех, кто хочет добавить в свои программы голос: от простых напоминаний до прототипов ассистентов и читалок. Попробуйте озвучить результаты своих скриптов — Python начнёт разговаривать с вами буквально.❤4
Отправка писем через SMTP с модулем
Представь: твой скрипт сам отправляет отчёт на почту каждое утро. Без ручного вмешательства, без «сейчас напишу письмо». В Python это делается в несколько строк с помощью модуля
## Базовая отправка письма
Простейший пример: отправим текстовое письмо через SMTP-сервер (например, Gmail):
Ключевые моменты:
-
-
-
Важно: многие почтовые сервисы требуют «пароль приложения», а не обычный пароль аккаунта.
## Форматирование письма по-взрослому
Реальные письма состоят из заголовков и тела, часто в нескольких форматах (текст + HTML). Удобнее собирать их через
Здесь
## Мини-чеклист по безопасности
- Не храни пароль в коде — используй переменные окружения или
- Всегда используй
- Тестируй сначала на своём ящике, а не на почте начальника.
Умение отправлять письма из скрипта открывает массу возможностей: автоматические отчёты, уведомления о падении сервиса, напоминания о дедлайнах — всё это можно делегировать Python.
smtplibПредставь: твой скрипт сам отправляет отчёт на почту каждое утро. Без ручного вмешательства, без «сейчас напишу письмо». В Python это делается в несколько строк с помощью модуля
smtplib.## Базовая отправка письма
SMTP — это протокол для отправки почты. В Python за него отвечает стандартный модуль smtplib, ничего ставить не нужно.Простейший пример: отправим текстовое письмо через SMTP-сервер (например, Gmail):
import smtplib
smtp_server = "smtp.gmail.com"
smtp_port = 587
sender_email = "you@example.com"
password = "your_app_password"
receiver_email = "friend@example.com"
message = """\
From: you@example.com
To: friend@example.com
Subject: Test email from Python
Hello! This is a test email sent via smtplib.
"""
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls() # шифруем соединение
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
Ключевые моменты:
-
starttls() — включает шифрование (TLS).-
login() — авторизация на почтовом сервере.-
sendmail() — отправка письма (от кого, кому, текст письма целиком).Важно: многие почтовые сервисы требуют «пароль приложения», а не обычный пароль аккаунта.
## Форматирование письма по-взрослому
Реальные письма состоят из заголовков и тела, часто в нескольких форматах (текст + HTML). Удобнее собирать их через
email-модуль.import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
smtp_server = "smtp.gmail.com"
smtp_port = 587
sender_email = "you@example.com"
password = "your_app_password"
receiver_email = "friend@example.com"
msg = MIMEMultipart("alternative")
msg["Subject"] = "Report from Python script"
msg["From"] = sender_email
msg["To"] = receiver_email
text_part = """\
Hi!
Here is your daily report in plain text.
"""
html_part = """\
<html>
<body>
<h2>Daily report</h2>
<p>This is a <b>HTML</b> version of the email.</p>
</body>
</html>
"""
msg.attach(MIMEText(text_part, "plain"))
msg.attach(MIMEText(html_part, "html"))
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(sender_email, password)
server.send_message(msg)
Здесь
send_message() принимает уже готовый объект письма, а не строку.## Мини-чеклист по безопасности
- Не храни пароль в коде — используй переменные окружения или
.env.- Всегда используй
starttls() или SMTP_SSL.- Тестируй сначала на своём ящике, а не на почте начальника.
Умение отправлять письма из скрипта открывает массу возможностей: автоматические отчёты, уведомления о падении сервиса, напоминания о дедлайнах — всё это можно делегировать Python.
👍4❤1🔥1
Парсинг аргументов командной строки:
---------------------------------------------------------------
Когда вы запускаете скрипт так:
Python передаёт все эти штуки после
###
Плюсы
- минимум магии, обычный список;
- полезен для одноразовых скриптов в несколько строк.
Минусы:
- сами пишете помощь (
- сами валидируете типы и диапазоны;
- сами обрабатываете ошибки и необязательные флаги.
Как только аргументов становится больше трёх — код превращается в лапшу с
###
Что делает
- автоматически создаёт
- преобразует типы (
- проверяет обязательные аргументы (
- поддерживает флаги (
- умеет подкоманды (
Пример запуска:
### Когда что выбирать
- Скрипт на 5–10 строк, пара позиционных аргументов, вы один раз запустите и забудете — можно использовать
- Всё, что будет жить дольше одного дня, запускаться другими людьми или иметь больше пары опций — берите
Цена входа минимальна, а взамен вы получаете автодокументацию, валидацию и гораздо более читаемый код.
По сути,
argparse против sys.argv---------------------------------------------------------------
Когда вы запускаете скрипт так:
python app.py --name Alice --count 3
Python передаёт все эти штуки после
app.py в ваш код. Есть два популярных способа это разобрать: голый массив sys.argv и модуль argparse. Они решают одну задачу, но ощущения от работы — как от велосипеда без тормозов и современного велосипеда с гидравликой.###
sys.argv: минимализм и ручная работаsys.argv — это просто список строк: первый элемент — имя файла, дальше — аргументы.import sys
def main():
args = sys.argv[1:] # пропускаем имя скрипта
if len(args) != 2:
print("Usage: python app.py <name> <count>")
sys.exit(1)
name = args[0]
try:
count = int(args[1])
except ValueError:
print("count must be integer")
sys.exit(1)
for _ in range(count):
print(f"Hello, {name}!")
if __name__ == "__main__":
main()
Плюсы
sys.argv:- минимум магии, обычный список;
- полезен для одноразовых скриптов в несколько строк.
Минусы:
- сами пишете помощь (
Usage: ...);- сами валидируете типы и диапазоны;
- сами обрабатываете ошибки и необязательные флаги.
Как только аргументов становится больше трёх — код превращается в лапшу с
if и try/except.###
argparse: встроенный “CLI-конструктор”argparse делает почти всё это за вас.import argparse
def build_parser():
parser = argparse.ArgumentParser(
description="Simple greeting script"
)
parser.add_argument(
"--name",
required=True,
help="Name to greet"
)
parser.add_argument(
"--count",
type=int,
default=1,
help="How many times to greet"
)
parser.add_argument(
"--loud",
action="store_true",
help="Use upper case for greeting"
)
return parser
def main():
parser = build_parser()
args = parser.parse_args()
message = f"Hello, {args.name}!"
if args.loud:
message = message.upper()
for _ in range(args.count):
print(message)
if __name__ == "__main__":
main()
Что делает
argparse:- автоматически создаёт
--help и аккуратный текст помощи;- преобразует типы (
type=int, float, pathlib.Path, свои функции);- проверяет обязательные аргументы (
required=True);- поддерживает флаги (
action="store_true");- умеет подкоманды (
git commit, git push-стайл).Пример запуска:
python app.py --help
python app.py --name Bob --count 3 --loud
### Когда что выбирать
- Скрипт на 5–10 строк, пара позиционных аргументов, вы один раз запустите и забудете — можно использовать
sys.argv.- Всё, что будет жить дольше одного дня, запускаться другими людьми или иметь больше пары опций — берите
argparse. Цена входа минимальна, а взамен вы получаете автодокументацию, валидацию и гораздо более читаемый код.
По сути,
sys.argv — это сырые данные, а argparse — готовый интерфейс командной строки. Начать стоит уметь с обоими, но писать серьёзные утилиты на “голом” sys.argv — как собирать шкаф без отвертки: возможно, но слишком больно.👍2🔥1
Создание тестов с pytest: простая проверка вашего кода
Если вы пишете код без тестов — вы просто верите, что он работает.
### Почему именно pytest
Минимальный порог входа: достаточно обычных `assert`.
Никаких классических «классов тестов» как в
Красивый вывод ошибок.
Работает и с маленькими скриптами, и с крупными проектами.
Установим:
### Первый тест за 2 минуты
Пусть есть файл
Создадим файл
Запуск в терминале:
### Что делает pytest удобным
1. Человеческие assert’ы
Никаких
Если тест упадет, вы получите наглядный дифф значений.
2. Параметризованные тесты
Одна функция теста — несколько наборов данных:
Так вы быстро покрываете типичные случаи и крайние значения.
3. Проверка исключений
Многие ошибки — это не только «плохой результат», но и «правильное исключение».
### Как встроить pytest в свою работу
1. Создавайте для каждого файла
2. Добавляйте тест при каждом исправлении бага: сначала тест, который падает, потом фикс.
3. Регулярно запускайте:
---
Если вы пишете код без тестов — вы просто верите, что он работает.
pytest позволяет вместо веры получить факты: быстро, просто и без боли.### Почему именно pytest
Минимальный порог входа: достаточно обычных `assert`.
Никаких классических «классов тестов» как в
unittest.Красивый вывод ошибок.
Работает и с маленькими скриптами, и с крупными проектами.
Установим:
pip install pytest
### Первый тест за 2 минуты
Пусть есть файл
math_utils.py:# math_utils.py
def add(a, b):
return a + b
def divide(a, b):
if b == 0:
raise ValueError("b must not be zero")
return a / b
Создадим файл
test_math_utils.py:# test_math_utils.py
from math_utils import add, divide
def test_add_two_positive_numbers():
assert add(2, 3) == 5
def test_add_with_negative_number():
assert add(-1, 4) == 3
def test_divide_normal_case():
assert divide(10, 2) == 5
def test_divide_by_zero_raises():
import pytest
with pytest.raises(ValueError):
divide(10, 0)
Запуск в терминале:
pytest
pytest сам найдет все файлы вида test_*.py и функции test_*.### Что делает pytest удобным
1. Человеческие assert’ы
Никаких
self.assertEqual(...). Пишем:def test_substring():
text = "hello world"
assert "world" in text
Если тест упадет, вы получите наглядный дифф значений.
2. Параметризованные тесты
Одна функция теста — несколько наборов данных:
import pytest
from math_utils import add
@pytest.mark.parametrize(
"a, b, expected",
[
(1, 2, 3),
(0, 0, 0),
(-1, 1, 0),
]
)
def test_add_parametrized(a, b, expected):
assert add(a, b) == expected
Так вы быстро покрываете типичные случаи и крайние значения.
3. Проверка исключений
Многие ошибки — это не только «плохой результат», но и «правильное исключение».
pytest.raises делает такой тест читабельным, как в примере с divide.### Как встроить pytest в свою работу
1. Создавайте для каждого файла
xxx.py файл test_xxx.py.2. Добавляйте тест при каждом исправлении бага: сначала тест, который падает, потом фикс.
3. Регулярно запускайте:
pytest -q
-q делает вывод короче и приятнее.---
pytest — это привычка, которая экономит часы отладки. Стоит один раз настроить минимальный набор тестов — и ваш Python-код перестает быть «черной коробкой» и начинает честно рассказывать, где он ломается.❤2
Как работать с очередями заданий с библиотекой
Представьте, что у вас есть список задач, которые нужно выполнить: скачать файлы, обработать данные, отправить письма. Если делать всё по очереди в одном потоке — программа “тупо” ждёт завершения каждой операции. Очереди заданий позволяют распределить работу между несколькими потоками и не потерять ни одной задачи.
В Python для этого есть модуль
---
### Базовый пример: очередь задач
Методы:
-
-
-
-
---
### Очередь + потоки: простой пул воркеров
Типичный сценарий: один поток генерирует задачи, несколько потоков их обрабатывают.
Ключевой момент: очередь сама заботится о синхронизации между потоками — вам не нужны явные блокировки.
---
### Другие типы очередей
-
-
Пример приоритетной очереди: задачи с меньшим числом выполняются раньше.
---
queueПредставьте, что у вас есть список задач, которые нужно выполнить: скачать файлы, обработать данные, отправить письма. Если делать всё по очереди в одном потоке — программа “тупо” ждёт завершения каждой операции. Очереди заданий позволяют распределить работу между несколькими потоками и не потерять ни одной задачи.
В Python для этого есть модуль
queue, который обеспечивает потокобезопасную очередь.---
### Базовый пример: очередь задач
import queue
task_queue = queue.Queue()
# добавляем задачи
task_queue.put("download_file_1")
task_queue.put("download_file_2")
task_queue.put("process_data")
while not task_queue.empty():
task = task_queue.get()
print("Handling:", task)
task_queue.task_done()
Queue() по умолчанию — неограниченная по размеру. Методы:
-
put(item) — добавить задачу-
get() — взять задачу (если очереди пустая — ждёт)-
task_done() — сообщить, что задача обработана-
join() — подождать, пока все задачи будут помечены как выполненные---
### Очередь + потоки: простой пул воркеров
Типичный сценарий: один поток генерирует задачи, несколько потоков их обрабатывают.
import queue
import threading
import time
task_queue = queue.Queue()
def worker(worker_id):
while True:
task = task_queue.get()
if task is None: # сигнал остановки
task_queue.task_done()
break
print(f"Worker {worker_id} handling {task}")
time.sleep(0.5) # имитация работы
task_queue.task_done()
# запускаем 3 обработчика
workers = []
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
t.start()
workers.append(t)
# добавляем задачи
for n in range(10):
task_queue.put(f"task_{n}")
# ждём завершения всех задач
task_queue.join()
# останавливаем воркеров
for _ in workers:
task_queue.put(None)
for t in workers:
t.join()
Ключевой момент: очередь сама заботится о синхронизации между потоками — вам не нужны явные блокировки.
---
### Другие типы очередей
queue предлагает не только обычную FIFO-очередь:-
queue.LifoQueue — стек (последний вошёл — первый вышел)-
queue.PriorityQueue — приоритетная очередьПример приоритетной очереди: задачи с меньшим числом выполняются раньше.
import queue
pq = queue.PriorityQueue()
pq.put((1, "low_priority"))
pq.put((0, "high_priority"))
pq.put((5, "very_low_priority"))
while not pq.empty():
priority, task = pq.get()
print(priority, task)
pq.task_done()
---
queue — это простой способ построить систему обработки заданий: от игрушечного пула потоков до мини-очереди задач внутри вашей программы. Понимание этих примитивов — отличный шаг к написанию более масштабных и отзывчивых приложений на Python.👍3🔥1