В данном гайде описаны основные правила выполнения лабораторных работ. Все примеры утрированы и используются для наглядной демонстрации правил.
Основные правила:
- Соблюдать PEP8.
- Весь код должен быть оформлен в виде функций и классов.
- Использовать type-hinting (указание в функциях и классах типов данных).
- Прописывать docstring (описание функций).
- Разделять программу на отдельные модули.
- Функции вызывать в условии
if __name__ == '__main__' - Использовать механизм обработки исключений.
Ниже эти правила разобраны на примерах.
Код необходимо оформлять в виде функций. Каждая функция должна выполнять одну задачу. Глобальный код недопустим, за исключением констант.
Рассмотрим пример программы, которая складывает два числа:
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() - объединяет все функции в программу.
Отсутствие явного указания типов данных может привести к ошибкам. Во избежание подобных проблем лучше использовать подсказки типов.
def sum(a: int, b: int) -> int:
return a + ba: 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 reslist - тип контейнера, 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 + bDocstring является документацией внутри кода. Она особенно полезна, когда с кодом работает несколько человек (или когда ваш код проверяет преподаватель...)
Пример 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Во втором случае мы явно прописываем каждый параметр и возвращаемое значение.
При импорте модуля выполняется весь его глобальный код. Предположим, у нас есть модуль 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()Даже если ваш код будет абсолютно правильным, при работе с данными, формат которых заранее неизвестен, могут возникать ошибки:
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()В итоге у нас получился отвратительно длинный правильный код, который будет понятен вашим коллегам (и преподавателю) и не вызовет ошибок.