← назад к разделу

Как только проверок становится больше одной, появляется повторение: тот же расчёт, те же три строки подготовки данных. Скопировать их в десять мест — значит потом менять в десяти местах. Функция решает это: код пишется один раз и вызывается по имени.

def стоимость(цена, количество): return цена * количество 4990, 2 стоимостьцена × кол-во 9980 написали один раз — зовём отовсюду расчёт, подготовка данных, своя проверка — всё это функции, которые перестают копироваться

Параметры входят, результат выходит через return. Меняется тело функции — меняется поведение везде, откуда её зовут.

Обязательно

Как выглядит функция

живой пример

def поздороваться():
    print("Привет")

поздороваться()      # вызов: теперь напечатается
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

def — «определяю функцию», дальше имя, скобки и двоеточие. Тело — со сдвигом. Пока функцию не вызвали, внутри неё ничего не происходит.

Отработать здесь: Своя функция

Параметры: одно и то же, но с разными данными

живой пример

def поздороваться(имя):
    print(f"Привет, {имя}")

поздороваться("Анна")     # Привет, Анна
поздороваться("Иван")     # Привет, Иван
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

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

Отработать здесь: Функция с параметром · Два параметра и расчёт

Возврат значения

Печатать изнутри функции почти всегда неправильно: результат нужен не на экране, а в коде — чтобы сравнить или передать дальше.

живой пример

# так не надо
def стоимость(цена, количество):
    print(цена * количество)

стоимость(4990, 2)
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

живой пример

# так надо
def стоимость(цена, количество):
    return цена * количество

итого = стоимость(4990, 2)
print(итого > 5000)
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Первая умеет только показать число человеку. Вторая отдаёт его в код: return заканчивает функцию и передаёт значение наружу, его кладут в переменную и сравнивают. Всё после return не выполнится.

Отработать здесь: Функция, которая отвечает «да» или «нет»

Значения по умолчанию

живой пример

def найти_заказы(заказы, статус="PAID"):
    return [з for з in заказы if з["status"] == статус]

заказы = [
    {"id": "ord-042", "status": "PAID"},
    {"id": "ord-043", "status": "CANCELLED"},
]

print(найти_заказы(заказы))                  # ищет PAID
print(найти_заказы(заказы, "CANCELLED"))     # ищет отменённые
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

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

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

живой пример

# так не надо
def добавить(элемент, куда=[]):
    куда.append(элемент)
    return куда

print(добавить("a"))
print(добавить("b"))     # ['a', 'b'] — след прошлого вызова
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

живой пример

# так надо
def добавить(элемент, куда=None):
    if куда is None:
        куда = []
    куда.append(элемент)
    return куда

print(добавить("a"))
print(добавить("b"))     # ['b'] — каждый вызов со своим списком
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Список в скобках создаётся один раз на всю программу, а не при каждом вызове, и копит значения между ними. С None он создаётся заново внутри функции.

Отработать здесь: Значение по умолчанию

Зачем это тестировщику

Подготовка данных. Заказ, на котором идёт проверка, собирают функцией: общее внутри, различия — параметрами.

живой пример

def заказ(статус="PAID", сумма=4990):
    return {"id": "ord-042", "status": статус, "total": сумма}

print(заказ())
print(заказ("CANCELLED", 890))
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Дальше в каждой проверке одна строка вместо трёх, а при изменении состава заказа правится одно место.

Своя проверка. Условие, которое повторяется, выносят и называют по смыслу:

живой пример

def сумма_сходится(заказ):
    посчитано = 0
    for позиция in заказ["items"]:
        посчитано += позиция["qty"] * позиция["price"]
    return посчитано == заказ["total"]

заказ = {"items": [{"qty": 1, "price": 4990}, {"qty": 2, "price": 890}], "total": 6770}
print(сумма_сходится(заказ))     # True
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Такая функция отвечает «да» или «нет»: читающий видит if not сумма_сходится(заказ) и понимает намерение без разбора арифметики.

Отработать здесь: Разложить проверку на две части

Как называть и как оформлять

Имя функции — глагол или вопрос: найти_заказы, добавить, сумма_сходится. В рабочем проекте пишут по-английски: find_orders, add, total_adds_up. Если имя получается длинным вроде сделать_всё_и_проверить — функция делает слишком много, её стоит разделить.

У питона есть общее соглашение об оформлении — PEP 8. В работе из него держат в голове четыре правила: отступ — четыре пробела (табы с пробелами не смешивают, питон ругнётся); имена функций и переменных — строчными через подчёркивание; вокруг = в присваивании пробелы, а у значения по умолчанию в скобках — нет; строка не длиннее 79 знаков по PEP 8, а в большинстве команд договариваются примерно о ста.

живой пример

# так не надо: имя ни о чём, пробелы где попало
def Проверка( заказ,минимум = 1000 ):
    return заказ [ "total" ]>=минимум

print(Проверка({"total": 4990}))
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

живой пример

# так надо: имя-вопрос, четыре пробела отступа, ровные пробелы
def сумма_не_меньше(заказ, минимум=1000):
    return заказ["total"] >= минимум

print(сумма_не_меньше({"total": 4990}))
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Оба варианта работают одинаково, но второй читается с первого взгляда — а код чаще читают, чем пишут. Следить за этим руками не нужно: редактор и форматировщик сами приводят файл к общему виду.

Дополнительно: при первом чтении можно пропустить

Глубже: класс и объект: зачем это в автотестахрасширенное

Функции хватает, пока данные и действия над ними живут отдельно. Как только появляется «корзина, в которую кладут товары и у которой спрашивают итог», удобнее держать данные и функции вместе. Для этого есть класс: описание того, из чего объект состоит и что умеет. Объект это конкретный экземпляр, созданный по классу.

живой пример

class Cart:
    def __init__(self):
        self.items = []

    def add(self, name, price):
        self.items.append({"name": name, "price": price})

    def total(self):
        return sum(item["price"] for item in self.items)


cart = Cart()
cart.add("чай", 300)
cart.add("кофе", 500)
print(len(cart.items), cart.total())  # 2 800
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

__init__ выполняется при создании объекта и задаёт начальные данные, self это сам объект, через который методы читают и меняют его поля. В автотестах классы встречаются постоянно: страница входа как класс с методами open, login и error_text, это Page Object, и тест читается как сценарий, а не как набор локаторов. Четыре слова про ООП, которые спрашивают: инкапсуляция, данные и методы вместе; наследование, класс берёт поведение родителя; полиморфизм, один метод по-разному у разных классов; абстракция, снаружи виден только нужный интерфейс. Разобраны они в статье про ООП в Python.

Коротко

  • def имя(параметры): объявляет функцию; пока её не вызвали, внутри ничего не происходит.
  • return отдаёт результат в код и заканчивает функцию; печатать изнутри — почти всегда ошибка.
  • Значение по умолчанию делает параметр необязательным; списком или словарём оно быть не должно.
  • В проверках функции закрывают две задачи: подготовка данных и своя проверка с понятным именем.
  • Имя — глагол или вопрос; слишком длинное имя означает, что функция делает слишком много.
  • Класс описывает данные и методы вместе, объект это экземпляр, __init__ задаёт начальные данные, self это сам объект; так устроен Page Object.

Что почитать дальше