Skip to content

Latest commit

 

History

History
276 lines (203 loc) · 8.79 KB

File metadata and controls

276 lines (203 loc) · 8.79 KB

Правила оформления лабораторных работ

В данном гайде описаны основные правила выполнения лабораторных работ. Все примеры утрированы и используются для наглядной демонстрации правил.

Основные правила:

  1. Соблюдать PEP8.
  2. Весь код должен быть оформлен в виде функций и классов.
  3. Использовать type-hinting (указание в функциях и классах типов данных).
  4. Прописывать docstring (описание функций).
  5. Разделять программу на отдельные модули.
  6. Функции вызывать в условии if __name__ == '__main__'
  7. Использовать механизм обработки исключений.

Ниже эти правила разобраны на примерах.

1. Функции

Код необходимо оформлять в виде функций. Каждая функция должна выполнять одну задачу. Глобальный код недопустим, за исключением констант.

Рассмотрим пример программы, которая складывает два числа:

a = int(input())
b = int(input())
c = a + b
print(c)

Она состоит только из глобального кода. Запишем правильный вариант с использованием функций:

def enter_number():
    return int(input())

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

def main():
    a = enter_number()
    b = enter_number()
    c = sum(a, b)
    print(c)

Каждая функция здесь выполняет свою задачу: enter_number() - получает число и преобразует его к int, sum() - складывает два значения, main() - объединяет все функции в программу.

2. Type-hinting

Отсутствие явного указания типов данных может привести к ошибкам. Во избежание подобных проблем лучше использовать подсказки типов.

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

a: int - тип данных аргумента функции

-> int - тип данных возвращаемого значения

Если функция ничего не возвращает, лучше указать типом возвращаемого значения None:

def print_hello(name: str) -> None:
    print(f"Hello, {name}!")

Если аргументом является контейнер, содержащий один тип данных, это тоже можно явно указать в подсказках:

def sum(numbers: list[int]) -> int:
    res = 0
    for num in numbers:
        res += num
    return res

list - тип контейнера, int - тип содержимого контейнера.

Если для аргумента может быть несколько допустимых типов, то используйте Union:

from typing import Union

def sum(a: Union[int, float], b: Union[int, float]) -> Union[int, float]:
    return a + b

или |:

def sum(a: int | float, b: int | float) -> int | float:
    return a + b

3. Docstring

Docstring является документацией внутри кода. Она особенно полезна, когда с кодом работает несколько человек (или когда ваш код проверяет преподаватель...)

Пример docstring для простых функций:

def sum(a: int, b: int) -> int:
    """
    Сумма a и b.
    """
    return a + b

Пример docstring для более сложных функций:

def sum(a: int, b: int) -> int:
    """
    Сумма чисел.
    :param a: первое слагаемое
    :param b: второе слагаемое
    :return: сумма
    """
    return a + b

Во втором случае мы явно прописываем каждый параметр и возвращаемое значение.

4. Старт программы

При импорте модуля выполняется весь его глобальный код. Предположим, у нас есть модуль my_sum.py, содержащий следующий код:

def sum(a: int, b: int) -> int:
    """
    Сумма a и b.
    """
    return a + b

print(sum(1, 2))

print(sum(1, 2)) - глобальный код. Импоритируем этот модуль в другой файл:

import my_sum

print(my_sum.sum(3, 4))

При запуске вместо одного результата будет напечатано сразу два - из импортированного файла и из запускаемого:

3
7

Для того, чтобы избежать подобной ситуации, следует использовать конструкцию if __name__ == '__main__':

def sum(a: int, b: int) -> int:
    """
    Сумма a и b.
    """
    return a + b


if __name__ == "__main__":
    print(sum(1, 2))

Теперь код print(sum(1, 2)) выполнится только если мы запустим файл my_sum.py, если же этот файл импортируется, то код выполнен не будет. Однако, не стоит забывать, что код, написанный после if __name__ == '__main__' всё ещё является глобальным кодом, поэтому в нём нежелательно писать слишком много. В идеале нужно создать отдельную функцию main() и вызывать только её:

if __name__ == "__main__":
    main()

5. Обработка исключений

Даже если ваш код будет абсолютно правильным, при работе с данными, формат которых заранее неизвестен, могут возникать ошибки:

a = int(input("Enter number: "))
Enter number: some string

ValueError: invalid literal for int() with base 10: 'some string'

В этом примере мы ввели строку вместо ожидаемого числа и получили исключение ValueError. Для того, чтобы предотвратить подобные ситуации, используйте механизм обработки исключений:

a = input("Enter number: ")
try:
    a = int(a)
except:
    print("Error!")
Enter number: some string
Error!

Если вы знаете исключение какого типа ожидать, то явно прописывайте его в блоке except - это даст пользователю больше информации об ошибке:

a = input("Enter number: ")
try:
    a = int(a)
except ValueError as exc:
    print(f"Error: {exc}")
Enter number: some string
Error: invalid literal for int() with base 10: 'some string'

Если вы хотите самостоятельно выбросить исключение, то используйте ключевое слово raise:

if not a.isdigit():
    raise ValueError("Value is not integer number")
Enter number: some string
Error: Value is not integer number

Итог

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

a = int(input())
b = int(input())
c = a + b
print(c)

И применим к нему все рассмотренные выше правила:

def enter_number() -> int:
    """
    Ввод числа с консоли.
    """
    num = input()
    if not num.isdigit():
        raise ValueError("Value is not integer number")
    return int(num)

def sum(a: int, b: int) -> int:
    """
    Сумма a и b.
    """
    return a + b

def main():
    try:
        a = enter_number()
        b = enter_number()
        c = sum(a, b)
        print(c)
    except ValueError as exc:
        print(f"Error: {exc}")

if __name__ == "__main__":
    main()

В итоге у нас получился отвратительно длинный правильный код, который будет понятен вашим коллегам (и преподавателю) и не вызовет ошибок.