uk
Feedback
Python для начинающих

Python для начинающих

Відкрити в Telegram

Python для начинающих

Показати більше
1 240
Підписники
Немає даних24 години
+17 днів
+130 день
Архів дописів
Создание тестов с pytest: простая проверка вашего кода Если вы пишете код без тестов — вы просто верите, что он работает. 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-код перестает быть «черной коробкой» и начинает честно рассказывать, где он ломается.

Создание тестов с pytest: простая проверка вашего кода
Создание тестов с pytest: простая проверка вашего кода

Парсинг аргументов командной строки: 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 — как собирать шкаф без отвертки: возможно, но слишком больно.

Парсинг аргументов командной строки: отличия argparse и sys.argv
Парсинг аргументов командной строки: отличия argparse и sys.argv

Отправка писем через SMTP с модулем 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.

Отправка писем через SMTP с модулем smtplib
Отправка писем через SMTP с модулем smtplib

Преобразование текста в речь с помощью pyttsx3: заставляем 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 начнёт разговаривать с вами буквально.

Преобразование текста в речь с помощью pyttsx3
Преобразование текста в речь с помощью pyttsx3

### Работа с временными зонами и UTC: почему 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 выглядит простым, но помогает избежать очень дорогих ошибок во времени, особенно когда пользователи живут в разных точках мира.

Работа с временными зонами и utc: использование pytz
Работа с временными зонами и utc: использование pytz

Создание календаря событий с помощью модуля 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 отлично подходит для прототипирования: быстрый текстовый календарь, базовая логика дат, дни недели, учет високосных лет — все уже есть «из коробки». Остается только обернуть это в интерфейс, который вам нужен.

Создание календаря событий с помощью модуля calendar
Создание календаря событий с помощью модуля calendar

Простое шифрование текста с базовым алгоритмом Caesar Когда-то, задолго до хакеров и брутфорса, шифрование могло выглядеть почти игрушкой. Один из самых простых и известных методов — шифр Цезаря. Он настолько прост, что идеально подходит для первых шагов в программировании и работе со строками в 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()
Такой простой проект полезен сразу в нескольких вещах: - работа со строками и символами; - понимание циклов и условий; - практика написания маленьких, переиспользуемых функций. Шифр Цезаря давно не считается надежным, но как учебный пример — это отличный старт, чтобы почувствовать, как из простых операций над символами рождается алгоритм шифрования.

Простое шифрование текста с базовым алгоритмом Caesar
Простое шифрование текста с базовым алгоритмом Caesar

Создание собственных исключений для понятной обработки ошибок Стандартные исключения 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)
Так обработчик ошибок видит не только текст, но и структурированные данные. --- ## Когда точно стоит заводить своё исключение - Ошибка связана с предметной областью (деньги, заказы, уровни в игре). - Вам нужно по‑разному реагировать на разные типы ошибок. - Вы пишете код, которым будут пользоваться другие разработчики. Если же ошибка — просто “не тот тип” или “не то значение”, часто достаточно стандартных исключений. Собственные исключения — это не “косметика”, а мощный инструмент архитектуры. Они превращают хаос “что-то упало” в чёткую систему сигналов, по которой программа может разумно реагировать на проблемы.

Создание собственных исключений для понятной обработки ошибок
Создание собственных исключений для понятной обработки ошибок

Как организовать модульную структуру проекта в Python Когда скрипт на 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
Это делает структуру понятной: глядя на импорт, сразу видно, откуда что приходит. --- ### Зачем все это? - Проще тестировать: можно импортировать один модуль и тестировать его изолированно. - Легче находить код: логика не размазана по одному файлу. - Удобно расширять проект: добавили новый модуль — остальное не пострадало. Модульная структура — это не «красиво», это «жить можно», когда код растет. Начать лучше сразу, даже если проект пока умещается в один файл.

Как организовать модульную структуру проекта в Python
Как организовать модульную структуру проекта в Python

Основы проверки кода с использованием pylint и flake8 Когда начинаешь писать на 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
--- Лучший момент начать использовать линтеры — сейчас. Они быстро превращают хаотичный учебный код в аккуратный и читаемый, а заодно приучают к хорошему стилю, почти как строгий, но полезный наставник.

Основы проверки кода с использованием pylint и flake8
Основы проверки кода с использованием pylint и flake8