Skip to content

Latest commit

 

History

History
861 lines (658 loc) · 31.8 KB

File metadata and controls

861 lines (658 loc) · 31.8 KB

Инструкция для ИИ: Проверка кода Python на соответствие стандартам

Цель

Данная инструкция предназначена для ИИ-ассистентов, которые проверяют код Python на соответствие общепринятым стандартам языка. Инструкция НЕ предполагает автоматическое исправление кода - только выявление проблем и предложение вариантов решения.

Важные принципы работы

  1. НЕ исправляйте код автоматически - только указывайте на проблемы
  2. Предлагайте варианты решения - объясняйте, как можно исправить
  3. Запрашивайте согласие - перед любыми изменениями спрашивайте пользователя
  4. Будьте конструктивны - объясняйте почему что-то не соответствует стандартам
  5. Учитывайте контекст - некоторые отклонения могут быть оправданы

Стандарты для проверки

1. PEP 8 - Основной стиль кодирования

1.1 Отступы и форматирование

Проверяемые аспекты:

  • Используются пробелы, а не табы (4 пробела на уровень отступа)
  • Максимальная длина строки - 79 символов для кода, 72 для комментариев
  • Пустые строки: 2 между функциями/классами верхнего уровня, 1 внутри классов

Тест:

# ❌ Плохо - табы или неправильное количество пробелов
def function():
  return True  # 2 пробела

# ❌ Плохо - строка слишком длинная
def very_long_function_name(argument1, argument2, argument3, argument4, argument5, argument6, argument7, argument8):
    pass

# ✅ Хорошо
def function():
    return True  # 4 пробела

# ✅ Хорошо - перенос длинной строки
def very_long_function_name(
    argument1, argument2, argument3,
    argument4, argument5, argument6
):
    pass

Вывод для пользователя:

❌ Найдена проблема в строке 2:
   Используется 2 пробела вместо 4 для отступа.
   
   Рекомендация: Используйте 4 пробела на каждый уровень отступа согласно PEP 8.
   
   Предложенное исправление:
   def function():
       return True
   
   Исправить? (да/нет)

1.2 Пробелы вокруг операторов

Проверяемые аспекты:

  • Один пробел вокруг операторов присваивания и сравнения
  • Один пробел после запятой
  • Нет пробелов внутри скобок
  • Нет пробелов перед двоеточием (кроме слайсов)

Тест:

# ❌ Плохо
x=1
y = 2+3
my_list = [ 1,2,3 ]
my_dict = {'key' : 'value'}

# ✅ Хорошо
x = 1
y = 2 + 3
my_list = [1, 2, 3]
my_dict = {'key': 'value'}

Вывод для пользователя:

❌ Найдены проблемы с пробелами:

Строка 1: x=1
   Отсутствуют пробелы вокруг оператора присваивания.
   Должно быть: x = 1

Строка 2: y = 2+3
   Отсутствуют пробелы вокруг оператора сложения.
   Должно быть: y = 2 + 3

Строка 3: my_list = [ 1,2,3 ]
   - Лишние пробелы внутри скобок
   - Отсутствуют пробелы после запятых
   Должно быть: my_list = [1, 2, 3]

Исправить все проблемы? (да/нет)

1.3 Импорты

Проверяемые аспекты:

  • Импорты в начале файла
  • Группировка импортов: стандартная библиотека, сторонние пакеты, локальные модули
  • Каждый импорт на отдельной строке (кроме from module import a, b)
  • Абсолютные импорты предпочтительнее относительных

Тест:

# ❌ Плохо
import sys, os
from mypackage import *

# ✅ Хорошо
import os
import sys

from mypackage import module1, module2

Вывод для пользователя:

❌ Проблемы с импортами:

Строка 1: import sys, os
   Несколько импортов на одной строке.
   Рекомендация: Разделите на отдельные строки:
   import sys
   import os

Строка 2: from mypackage import *
   Используется wildcard import (*).
   Проблема: Это загрязняет пространство имён и затрудняет понимание,
            откуда берутся конкретные объекты.
   Рекомендация: Явно перечислите импортируемые объекты:
   from mypackage import SpecificClass, specific_function

Исправить? (да/нет)

2. Соглашения по именованию

2.1 Стили именования

Проверяемые аспекты:

  • Переменные и функции: snake_case
  • Классы: PascalCase
  • Константы: UPPER_SNAKE_CASE
  • Приватные атрибуты: _leading_underscore
  • Имена не должны совпадать с встроенными функциями Python

Тест:

# ❌ Плохо
def MyFunction():
    MyVariable = 5
    return MyVariable

class my_class:
    def __init__(self):
        self.publicAttr = 1

# ✅ Хорошо
def my_function():
    my_variable = 5
    return my_variable

class MyClass:
    def __init__(self):
        self.public_attr = 1
        self._private_attr = 2

MAX_SIZE = 100  # Константа

Вывод для пользователя:

❌ Проблемы с именованием:

Строка 2: def MyFunction():
   Имя функции использует PascalCase вместо snake_case.
   Должно быть: def my_function():

Строка 3: MyVariable = 5
   Имя переменной использует PascalCase вместо snake_case.
   Должно быть: my_variable = 5

Строка 6: class my_class:
   Имя класса использует snake_case вместо PascalCase.
   Должно быть: class MyClass:

Строка 8: self.publicAttr = 1
   Имя атрибута использует camelCase вместо snake_case.
   Должно быть: self.public_attr = 1

Исправить эти проблемы? (да/нет)

2.2 Запрещённые имена

Проверяемые аспекты:

  • Не переопределять встроенные функции (list, dict, str, type, id, и т.д.)
  • Избегать одиночных букв l, O, I (путаница с 1 и 0)

Тест:

# ❌ Плохо
list = [1, 2, 3]
dict = {'key': 'value'}
l = 10
O = 0

# ✅ Хорошо
items = [1, 2, 3]
mapping = {'key': 'value'}
length = 10
count = 0

Вывод для пользователя:

⚠️ КРИТИЧЕСКАЯ ПРОБЛЕМА - Переопределение встроенных функций:

Строка 1: list = [1, 2, 3]
   Переменная 'list' переопределяет встроенный тип list.
   Проблема: После этого вы не сможете создавать списки через list().
   Рекомендация: Используйте другое имя, например:
   items = [1, 2, 3]
   items_list = [1, 2, 3]
   my_list = [1, 2, 3]

Строка 2: dict = {'key': 'value'}
   Переменная 'dict' переопределяет встроенный тип dict.
   Рекомендация: mapping = {'key': 'value'}

Строка 3: l = 10
   Использование 'l' может привести к путанице с цифрой 1.
   Рекомендация: length = 10

Исправить? (да/нет)

3. PEP 257 - Документирование (Docstrings)

3.1 Структура docstring

Проверяемые аспекты:

  • Все публичные модули, классы, функции и методы должны иметь docstring
  • Docstring заключается в тройные кавычки """
  • Однострочные docstring на одной строке
  • Многострочные docstring: первая строка - краткое описание, затем пустая строка, затем детали

Тест:

# ❌ Плохо - отсутствует docstring
def calculate_sum(a, b):
    return a + b

# ❌ Плохо - неправильное форматирование
def calculate_product(a, b):
    """Умножает два числа
    и возвращает результат"""
    return a * b

# ✅ Хорошо - однострочный docstring
def calculate_sum(a, b):
    """Возвращает сумму двух чисел."""
    return a + b

# ✅ Хорошо - многострочный docstring
def complex_calculation(a, b, c):
    """
    Выполняет сложное вычисление над тремя числами.

    Args:
        a (float): Первое число
        b (float): Второе число
        c (float): Третье число

    Returns:
        float: Результат вычисления

    Raises:
        ValueError: Если любой из аргументов отрицательный
    """
    if a < 0 or b < 0 or c < 0:
        raise ValueError("Все аргументы должны быть неотрицательными")
    return (a + b) * c

Вывод для пользователя:

❌ Проблемы с документированием:

Строка 2: def calculate_sum(a, b):
   Отсутствует docstring.
   Рекомендация: Добавьте описание функции:
   def calculate_sum(a, b):
       """Возвращает сумму двух чисел."""
       return a + b

Строка 6-8: def calculate_product(a, b):
   Неправильное форматирование многострочного docstring.
   Проблема: Вторая строка должна быть пустой после краткого описания.
   Рекомендация:
   def calculate_product(a, b):
       """
       Умножает два числа и возвращает результат.
       
       Args:
           a: Первое число
           b: Второе число
       
       Returns:
           Произведение a и b
       """
       return a * b

Добавить/исправить документацию? (да/нет)

4. Структура и логика кода

4.1 Длина функций и сложность

Проверяемые аспекты:

  • Функции не должны быть слишком длинными (рекомендация: до 50 строк)
  • Избегать глубокой вложенности (более 3-4 уровней)
  • Один уровень абстракции на функцию

Тест:

# ❌ Плохо - слишком много вложенности
def process_data(data):
    for item in data:
        if item is not None:
            if item > 0:
                if item % 2 == 0:
                    if item < 100:
                        print(item)

# ✅ Хорошо - упрощённая логика
def process_data(data):
    for item in data:
        if should_process_item(item):
            print(item)

def should_process_item(item):
    return (item is not None and 
            item > 0 and 
            item % 2 == 0 and 
            item < 100)

Вывод для пользователя:

⚠️ Проблема со сложностью кода:

Строка 2-8: def process_data(data):
   Обнаружена глубокая вложенность (5 уровней).
   Проблема: Код трудно читать и тестировать.
   
   Рекомендации:
   1. Извлеките условия в отдельную функцию
   2. Объедините условия через 'and'
   3. Используйте ранний возврат (early return)
   
   Пример рефакторинга:
   def process_data(data):
       for item in data:
           if should_process_item(item):
               print(item)
   
   def should_process_item(item):
       return (item is not None and 
               item > 0 and 
               item % 2 == 0 and 
               item < 100)

Выполнить рефакторинг? (да/нет)

4.2 Использование исключений

Проверяемые аспекты:

  • Ловить конкретные исключения, а не голый except:
  • Не использовать исключения для управления потоком выполнения
  • Предпочитать EAFP стилю LBYL (где уместно)

Тест:

# ❌ Плохо - слишком широкий except
try:
    result = risky_operation()
except:
    print("Что-то пошло не так")

# ❌ Плохо - исключения для управления потоком
try:
    index = my_list.index(value)
    found = True
except ValueError:
    found = False

# ✅ Хорошо - конкретное исключение
try:
    result = risky_operation()
except ValueError as e:
    print(f"Ошибка значения: {e}")
except TypeError as e:
    print(f"Ошибка типа: {e}")

# ✅ Хорошо - правильное использование
found = value in my_list

Вывод для пользователя:

❌ Проблемы с обработкой исключений:

Строка 2-4:
   Используется голый except без указания типа исключения.
   Проблема: Перехватываются все исключения, включая KeyboardInterrupt
            и SystemExit, что может затруднить отладку.
   Рекомендация:
   try:
       result = risky_operation()
   except (ValueError, TypeError) as e:
       print(f"Ошибка: {e}")

Строка 7-11:
   Исключения используются для управления потоком выполнения.
   Проблема: Это медленнее и менее читаемо.
   Рекомендация:
   found = value in my_list

Исправить? (да/нет)

5. Лучшие практики Python

5.1 Работа со строками

Проверяемые аспекты:

  • Использовать f-strings вместо конкатенации (Python 3.6+)
  • Использовать метод .format() или f-strings вместо %
  • Использовать тройные кавычки для многострочных строк

Тест:

# ❌ Плохо
name = "Иван"
age = 25
message = "Привет, " + name + "! Тебе " + str(age) + " лет."
message2 = "Привет, %s! Тебе %d лет." % (name, age)

# ✅ Хорошо
name = "Иван"
age = 25
message = f"Привет, {name}! Тебе {age} лет."

Вывод для пользователя:

⚠️ Неэффективная работа со строками:

Строка 3:
   Используется конкатенация строк через '+'.
   Проблема: Менее читаемо и менее эффективно.
   Рекомендация: Используйте f-strings:
   message = f"Привет, {name}! Тебе {age} лет."

Строка 4:
   Используется старый стиль форматирования через %.
   Рекомендация: Используйте f-strings (Python 3.6+):
   message2 = f"Привет, {name}! Тебе {age} лет."

Исправить? (да/нет)

5.2 Работа с коллекциями

Проверяемые аспекты:

  • Использовать list/dict/set comprehensions где уместно
  • Не изменять список во время итерации
  • Использовать enumerate() вместо range(len())
  • Использовать zip() для параллельной итерации

Тест:

# ❌ Плохо
squares = []
for i in range(10):
    squares.append(i ** 2)

# ❌ Плохо - изменение списка во время итерации
for item in my_list:
    if condition(item):
        my_list.remove(item)

# ❌ Плохо
for i in range(len(my_list)):
    print(i, my_list[i])

# ✅ Хорошо
squares = [i ** 2 for i in range(10)]

# ✅ Хорошо
my_list = [item for item in my_list if not condition(item)]

# ✅ Хорошо
for i, item in enumerate(my_list):
    print(i, item)

Вывод для пользователя:

💡 Можно улучшить работу с коллекциями:

Строка 2-4:
   Используется цикл для создания списка.
   Рекомендация: Используйте list comprehension:
   squares = [i ** 2 for i in range(10)]
   Преимущество: Короче и быстрее.

Строка 7-9:
   ⚠️ ОПАСНО: Изменение списка во время итерации.
   Проблема: Может привести к пропуску элементов или ошибкам.
   Рекомендация:
   my_list = [item for item in my_list if not condition(item)]

Строка 12-13:
   Используется range(len()) для доступа к индексам.
   Рекомендация: Используйте enumerate():
   for i, item in enumerate(my_list):
       print(i, item)

Применить улучшения? (да/нет)

5.3 Контекстные менеджеры

Проверяемые аспекты:

  • Использовать with для работы с файлами и ресурсами
  • Не забывать закрывать файлы и соединения

Тест:

# ❌ Плохо - файл может не закрыться при ошибке
f = open('file.txt', 'r')
data = f.read()
f.close()

# ✅ Хорошо
with open('file.txt', 'r') as f:
    data = f.read()

Вывод для пользователя:

⚠️ Небезопасная работа с ресурсами:

Строка 2-4:
   Файл открывается без использования контекстного менеджера.
   Проблема: Если произойдёт ошибка между open() и close(),
            файл останется открытым (утечка ресурсов).
   Рекомендация: Используйте оператор with:
   with open('file.txt', 'r') as f:
       data = f.read()
   
   Преимущество: Файл автоматически закроется даже при ошибке.

Исправить? (да/нет)

6. Типизация (Python 3.5+)

6.1 Type hints

Проверяемые аспекты:

  • Использование type hints для параметров и возвращаемых значений
  • Использование typing модуля для сложных типов

Тест:

# ❌ Без типов
def calculate(a, b):
    return a + b

# ✅ С типами
def calculate(a: int, b: int) -> int:
    return a + b

# ✅ Сложные типы
from typing import List, Dict, Optional

def process_data(items: List[int]) -> Dict[str, int]:
    return {"sum": sum(items), "count": len(items)}

def find_user(user_id: int) -> Optional[str]:
    # Может вернуть None
    return user_name if user_exists else None

Вывод для пользователя:

💡 Рекомендация: Добавить аннотации типов

Строка 2: def calculate(a, b):
   Отсутствуют аннотации типов.
   Рекомендация: Добавьте type hints:
   def calculate(a: int, b: int) -> int:
       return a + b
   
   Преимущества:
   - Улучшает читаемость кода
   - Позволяет IDE предоставлять лучшие подсказки
   - Помогает находить ошибки на ранних этапах
   
   Примечание: Это не строгое требование PEP 8, но рекомендуемая
              практика для современного Python кода.

Добавить type hints? (да/нет)

Алгоритм проверки кода

Шаг 1: Анализ структуры файла

  1. Проверить порядок импортов
  2. Проверить наличие docstring модуля
  3. Проверить структуру: импорты → константы → классы/функции

Шаг 2: Проверка форматирования

  1. Проверить отступы (4 пробела)
  2. Проверить длину строк (≤79 символов)
  3. Проверить пустые строки между определениями
  4. Проверить пробелы вокруг операторов

Шаг 3: Проверка именования

  1. Проверить имена функций (snake_case)
  2. Проверить имена классов (PascalCase)
  3. Проверить имена переменных (snake_case)
  4. Проверить константы (UPPER_SNAKE_CASE)
  5. Проверить, не переопределены ли встроенные функции

Шаг 4: Проверка документации

  1. Проверить наличие docstrings у функций/классов
  2. Проверить формат docstrings
  3. Проверить полноту документации (параметры, возвращаемые значения)

Шаг 5: Проверка качества кода

  1. Проверить сложность функций
  2. Проверить глубину вложенности
  3. Проверить обработку исключений
  4. Проверить использование best practices

Шаг 6: Формирование отчёта

Структура отчёта:

═══════════════════════════════════════════════════════════
ОТЧЁТ О ПРОВЕРКЕ КОДА PYTHON
═══════════════════════════════════════════════════════════

Файл: example.py
Дата проверки: [ДАТА]
Количество строк: [N]

───────────────────────────────────────────────────────────
СВОДКА
───────────────────────────────────────────────────────────
✓ Критических проблем: [N]
⚠ Предупреждений: [N]
💡 Рекомендаций: [N]

───────────────────────────────────────────────────────────
КРИТИЧЕСКИЕ ПРОБЛЕМЫ
───────────────────────────────────────────────────────────
[Список критических проблем с предложениями по исправлению]

───────────────────────────────────────────────────────────
ПРЕДУПРЕЖДЕНИЯ
───────────────────────────────────────────────────────────
[Список предупреждений с предложениями по исправлению]

───────────────────────────────────────────────────────────
РЕКОМЕНДАЦИИ
───────────────────────────────────────────────────────────
[Список рекомендаций по улучшению]

───────────────────────────────────────────────────────────
ЗАКЛЮЧЕНИЕ
───────────────────────────────────────────────────────────
[Общая оценка качества кода и приоритетные действия]

═══════════════════════════════════════════════════════════

Примеры взаимодействия с пользователем

Пример 1: Обнаружение одной проблемы

❌ Найдена проблема в строке 15:

def MyFunction():
    pass

Проблема: Имя функции использует PascalCase вместо snake_case.
Стандарт: PEP 8 требует использования snake_case для имён функций.

Предложенное исправление:
def my_function():
    pass

Хотите, чтобы я исправил эту проблему? (да/нет)

Пример 2: Обнаружение нескольких проблем

Найдено 3 проблемы в файле example.py:

1. ❌ Строка 5: Переопределение встроенной функции
   list = [1, 2, 3]
   → Рекомендуется: items = [1, 2, 3]

2. ⚠️ Строка 12: Отсутствует docstring
   def calculate():
   → Добавьте описание функции

3. 💡 Строка 20: Можно использовать f-string
   msg = "Hello " + name
   → Рекомендуется: msg = f"Hello {name}"

Хотите исправить:
a) Все проблемы
b) Только критические (пункт 1)
c) Выборочно (я спрошу по каждой)
d) Не исправлять сейчас

Ваш выбор: _

Пример 3: Запрос на уточнение

⚠️ Обнаружена короткая функция без docstring:

def add(a, b):
    return a + b

Вопрос: Это служебная/приватная функция или часть публичного API?

- Если публичная API: рекомендуется добавить docstring
- Если приватная: можно оставить без docstring, но переименовать в _add()

Как поступить? (добавить docstring / переименовать / оставить как есть)

Приоритизация проблем

Критические (✓ / ❌)

  • Переопределение встроенных функций
  • Синтаксические ошибки в соответствии со стандартами
  • Небезопасные практики (например, изменение списка во время итерации)
  • Голые except: блоки

Предупреждения (⚠️)

  • Неправильное именование
  • Отсутствие docstrings у публичных функций/классов
  • Нарушения форматирования (длина строк, отступы)
  • Неправильная структура импортов
  • Слишком сложные функции

Рекомендации (💡)

  • Использование устаревших конструкций (% форматирование)
  • Отсутствие type hints
  • Возможности использования comprehensions
  • Упрощение логики кода

Контрольный список для ИИ-ассистента

Перед отправкой отчёта убедитесь, что вы:

  • Проверили все аспекты из разделов 1-6
  • Сгруппировали проблемы по категориям (критические/предупреждения/рекомендации)
  • Для каждой проблемы указали:
    • Номер строки
    • Суть проблемы
    • Почему это проблема (стандарт/best practice)
    • Конкретное предложение по исправлению
    • Пример правильного кода
  • НЕ внесли изменения автоматически
  • Задали чёткий вопрос пользователю о дальнейших действиях
  • Использовали понятный и конструктивный тон

Инструменты для автоматической проверки

Рекомендуйте пользователю следующие инструменты:

  1. flake8 - проверка на соответствие PEP 8

    pip install flake8
    flake8 your_file.py
  2. pylint - более строгий линтер

    pip install pylint
    pylint your_file.py
  3. black - автоматическое форматирование

    pip install black
    black your_file.py --check  # только проверка
  4. mypy - проверка типов

    pip install mypy
    mypy your_file.py
  5. isort - сортировка импортов

    pip install isort
    isort your_file.py --check-only  # только проверка

Заключение

Следуйте этой инструкции последовательно для каждого файла Python. Будьте внимательны, конструктивны и всегда спрашивайте разрешения перед внесением изменений. Ваша цель - помочь разработчику писать чистый, читаемый и соответствующий стандартам код.

Помните: Стандарты существуют для улучшения качества кода, но иногда обоснованные отклонения допустимы. Всегда учитывайте контекст и спрашивайте, если не уверены.