Параметры и аргументы функций в Python

Обновлено:

1. Передача данных в функцию

Ранее мы создавали функции, которые не получали никаких данных:

def get_greeting():
    return 'Hello'

Такая функция при каждом вызове возвращает один и тот же результат:

print(get_greeting())
print(get_greeting())

Результат:

Hello
Hello

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

Передаваемые данные называются аргументами.


2. Параметр и аргумент

Важно различать два термина:

  • параметр;
  • аргумент.

Рассмотрим определение функции:

def calculate_square_area(side_length):
    return side_length ** 2

Имя:

side_length

в определении функции называется параметром.

Теперь вызовем функцию:

calculate_square_area(5)

Число:

5

называется аргументом.

Кратко:

Параметр указывается при определении функции.
Аргумент передаётся при вызове функции.

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


3. Первый параметр функции

Создадим функцию для нахождения площади квадрата:

def calculate_square_area(side_length):
    return side_length ** 2

Функция должна получить длину одной стороны квадрата.

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

side_length

После этого функция возведёт длину стороны во вторую степень и вернёт площадь.


4. Вызов функции с одним аргументом

result = calculate_square_area(5)

print(result)

Результат:

25

При вызове:

calculate_square_area(5)

число 5 передаётся в функцию.

Внутри функции параметр:

side_length

начинает ссылаться на объект 5.

Функция выполняет:

5 ** 2

и возвращает:

25

5. Как аргумент попадает в параметр

Определение функции:

def calculate_square_area(side_length):
    return side_length ** 2

Вызов:

calculate_square_area(5)

Упрощённо работу можно представить так:

Аргумент:

5

передаётся в параметр:

side_length

Во время вызова:

side_length ─────> 5

После этого тело функции использует имя side_length.


6. Одна функция и разные аргументы

print(calculate_square_area(5))
print(calculate_square_area(6))
print(calculate_square_area(9))

Результат:

25
36
81

Мы написали алгоритм только один раз:

side_length ** 2

Но использовали его для разных значений.


7. Переиспользование функции

Без функции пришлось бы несколько раз писать:

5 ** 2
6 ** 2
9 ** 2

С функцией мы используем понятное имя:

calculate_square_area(5)
calculate_square_area(6)
calculate_square_area(9)

Имя функции сразу сообщает, что программа рассчитывает площадь квадрата.


8. Обязательный параметр

Параметр:

side_length

является обязательным.

Это означает, что при вызове функции нужно передать значение для этого параметра.

Правильно:

calculate_square_area(5)

Неправильно:

calculate_square_area()

Во втором варианте функция не получила длину стороны.

Будет ошибка:

TypeError

9. Вызов без обязательного аргумента

def calculate_square_area(side_length):
    return side_length ** 2


calculate_square_area()

Python сообщает, что не было передано обязательное значение для параметра:

side_length

Функция не может выполнить:

side_length ** 2

потому что значение side_length отсутствует.


10. Сравнение с функцией len()

Функция len() тоже ожидает один обязательный аргумент.

Правильно:

len('Python')

Неправильно:

len()

Вызов без аргумента приведёт к ошибке TypeError.

Наша функция работает похожим образом:

calculate_square_area(5)

Правильно.

calculate_square_area()

Ошибка.


11. Слишком много аргументов

Функция определена с одним параметром:

def calculate_square_area(side_length):
    return side_length ** 2

Поэтому в наших примерах она ожидает ровно один аргумент.

Неправильно:

calculate_square_area(5, 10)

Переданы два значения, но функция подготовила только один параметр.

Будет ошибка:

TypeError

12. Позиционная передача аргумента

На этом уроке передаём аргументы по позиции:

calculate_square_area(5)

Первый переданный аргумент попадает в первый параметр функции.

В определении есть один параметр:

def calculate_square_area(side_length):

В вызове есть один аргумент:

calculate_square_area(5)

Поэтому число 5 попадает в side_length.

Другие способы передачи аргументов будут изучаться позже.


13. Какие объекты принимает функция

Внутри функции используется оператор:

**
def calculate_square_area(side_length):
    return side_length ** 2

На данном этапе в эту функцию передаём:

  • int;
  • float.

Пример с целым числом:

calculate_square_area(5)

Результат:

25

Пример с вещественным числом:

calculate_square_area(2.5)

Результат:

6.25

14. Ошибка при неподходящем типе

Передадим строку:

calculate_square_area('5')

Внутри функции Python попытается выполнить:

'5' ** 2

Строка не поддерживает такую операцию.

Будет ошибка:

TypeError

Причина ошибки находится не в самом механизме передачи аргумента.

Аргумент успешно попал в функцию, но операция внутри функции не умеет работать с полученным объектом.


15. Функция пока не проверяет тип

Наша функция записана так:

def calculate_square_area(side_length):
    return side_length ** 2

В ней нет специальных проверок.

Python не запрещает передать строку заранее.

Ошибка появляется только тогда, когда выполняется неподдерживаемая операция:

side_length ** 2

В будущем мы познакомимся с:

  • аннотациями типов;
  • документацией функций;
  • проверками полученных данных;
  • обработкой ошибок.

Сейчас определяем ожидаемые типы по операциям внутри функции.


16. Результат функции можно сохранить

first_area = calculate_square_area(5)
second_area = calculate_square_area(8)

print(first_area)
print(second_area)

Результат:

25
64

Каждый вызов возвращает отдельный результат.


17. Результат можно сразу передать в print()

Необязательно сначала создавать переменную:

print(calculate_square_area(6))

Сначала вызывается:

calculate_square_area(6)

Функция возвращает:

36

После этого внешний print() выводит результат:

36

18. Вложенный вызов функции

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

print(calculate_square_area(calculate_square_area(5)))

Сначала выполняется внутренняя функция:

calculate_square_area(5)

Результат:

25

После этого получается:

calculate_square_area(25)

Результат:

625

Именно это число выводит print().


19. Порядок выполнения вложенного вызова

calculate_square_area(calculate_square_area(5))

Первый шаг:

calculate_square_area(5)

Результат:

25

Второй шаг:

calculate_square_area(25)

Результат:

625

Функции внутри скобок выполняются раньше внешней функции.


20. Функция с двумя параметрами

Создадим функцию сложения двух чисел:

def add_numbers(first_number, second_number):
    return first_number + second_number

В определении находятся два параметра:

first_number
second_number

При вызове нужно передать два аргумента.


21. Вызов функции с двумя аргументами

result = add_numbers(10, 5)

print(result)

Результат:

15

Первый аргумент:

10

попадает в первый параметр:

first_number

Второй аргумент:

5

попадает во второй параметр:

second_number

22. Позиции аргументов

add_numbers(10, 5)

Упрощённая схема:

10 ─────> first_number
5  ─────> second_number

Затем функция выполняет:

first_number + second_number

То есть:

10 + 5

23. Разные вызовы add_numbers()

print(add_numbers(10, 5))
print(add_numbers(2, 3))
print(add_numbers(100, 250))

Результат:

15
5
350

Алгоритм внутри функции не меняется.

Меняются только передаваемые аргументы.


24. Недостаточно аргументов

Функция ожидает два обязательных значения:

def add_numbers(first_number, second_number):
    return first_number + second_number

Неправильно:

add_numbers(10)

Значение для first_number передано.

Значение для second_number отсутствует.

Будет ошибка:

TypeError

25. Слишком много аргументов

Неправильно:

add_numbers(10, 5, 3)

Функция подготовила два параметра:

first_number
second_number

Но получила три аргумента.

Будет ошибка:

TypeError

26. Аргументы являются обязательными

В нашей функции:

def add_numbers(first_number, second_number):
    return first_number + second_number

оба параметра являются обязательными.

Для успешного вызова передаём ровно два аргумента:

add_numbers(10, 5)

Значения по умолчанию на этом уроке не рассматриваются.


27. Позиция особенно важна для разных действий

При сложении перестановка чисел не изменит результат:

add_numbers(10, 5)

и:

add_numbers(5, 10)

оба вызова вернут:

15

Но это особенность сложения.

Рассмотрим вычитание:

def subtract_numbers(first_number, second_number):
    return first_number - second_number

28. Позиции в функции вычитания

subtract_numbers(10, 5)

Результат:

5

Но:

subtract_numbers(5, 10)

Результат:

-5

Первый аргумент всегда попадает в первый параметр, а второй — во второй.

Поэтому порядок позиционных аргументов имеет значение.


29. Ожидаемые типы для add_numbers()

Внутри функции используется оператор:

+
def add_numbers(first_number, second_number):
    return first_number + second_number

Для числового сложения передаём int и float:

add_numbers(10, 5)
add_numbers(2.5, 3.5)
add_numbers(10, 2.5)

30. Операция определяет поведение функции

Python смотрит не на название функции, а на операции внутри неё.

Например:

add_numbers('Py', 'thon')

технически вернёт:

'Python'

Потому что оператор + умеет соединять две строки.

Но по смыслу наша функция предназначена для сложения чисел.

Поэтому передаём в неё числовые объекты.


31. Несовместимые типы

add_numbers(10, '5')

Внутри функции получится:

10 + '5'

Число нельзя сложить со строкой.

Будет ошибка:

TypeError

Даже собственная функция может завершиться с ошибкой, если переданные объекты не поддерживают действия из её тела.


32. Использование результата нескольких функций

value = add_numbers(7, 3) * add_numbers(2, 3)

print(value)

Сначала:

add_numbers(7, 3)

возвращает:

10

После этого:

add_numbers(2, 3)

возвращает:

5

Остаётся:

10 * 5

Результат:

50

33. Параметры находятся в локальной области

Создадим функцию:

def show_local_names(first_name):
    local_number = 1
    print('local:', dir())

Вызовем её:

show_local_names('John')

Среди локальных имён будут:

first_name
local_number

Параметр функции является локальным именем.


34. Что происходит при вызове

show_local_names('John')

Строка:

'John'

передаётся в параметр:

first_name

Во время вызова внутри локальной области появляется связь:

first_name ─────> 'John'

Затем функция создаёт ещё одно локальное имя:

local_number ─────> 1

35. Параметр похож на локальную переменную

Параметр:

first_name

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

def show_user_name(first_name):
    print(first_name)

Вызов:

show_user_name('John')

Результат:

John

36. Параметры не появляются в глобальной области

def show_user_name(first_name):
    print(first_name)

Имя:

first_name

относится к локальной области функции.

После завершения вызова нельзя обратиться к нему в глобальной области:

print(first_name)

Будет ошибка:

NameError

37. Параметр создаётся при каждом вызове

def show_user_name(first_name):
    print(first_name)

Первый вызов:

show_user_name('John')

Во время него локальный параметр ссылается на строку 'John'.

Второй вызов:

show_user_name('Anna')

Во время нового вызова создаётся новая локальная область, и параметр ссылается на строку 'Anna'.


38. У каждого вызова своя локальная область

show_user_name('John')
show_user_name('Anna')

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

Для каждого вызова создаётся собственная локальная область.

После завершения вызова обычные локальные связи освобождаются.


39. Несколько параметров в локальной области

def show_user_data(first_name, age):
    local_message = 'User data'
    print('local:', dir())

Вызов:

show_user_data('John', 25)

Среди локальных имён будут:

age
first_name
local_message

В локальную область попадают:

  • параметры;
  • переменные, созданные в теле функции.

40. Глобальная область не получает параметры

global_message = 'Program started'


def show_user_data(first_name, age):
    local_message = 'User data'
    print('local:', dir())


show_user_data('John', 25)

print('global:', dir())

Среди глобальных имён будут:

global_message
show_user_data

Параметры:

first_name
age

и локальная переменная:

local_message

не становятся глобальными.


41. Функция с тремя параметрами

Функция может принимать больше двух аргументов:

def add_three_numbers(
    first_number,
    second_number,
    third_number,
):
    return first_number + second_number + third_number

Здесь находятся три обязательных параметра.


42. Вызов с тремя аргументами

result = add_three_numbers(5, 2, 10)

print(result)

Результат:

17

Соответствие по позициям:

5  ─────> first_number
2  ─────> second_number
10 ─────> third_number

43. Другой вызов той же функции

print(add_three_numbers(8, 5, 1))

Результат:

14

Мы снова используем тот же алгоритм, но передаём другие значения.


44. Нужно передать точное количество аргументов

Функция определена с тремя обязательными параметрами:

def add_three_numbers(
    first_number,
    second_number,
    third_number,
):
    return first_number + second_number + third_number

Правильно:

add_three_numbers(5, 2, 10)

Неправильно:

add_three_numbers(5, 2)

Неправильно:

add_three_numbers(5, 2, 10, 20)

Оба неправильных вызова приведут к TypeError.


45. Перенос длинного списка параметров

Если определение функции не помещается в одну короткую строку, параметры можно перенести:

def add_three_numbers(
    first_number,
    second_number,
    third_number,
):
    return first_number + second_number + third_number

Каждый параметр записан на отдельной строке.

После последнего параметра поставлена запятая.

Такой вариант удобно читать.

Для короткой функции допустима одна строка:

def add_three_numbers(first_number, second_number, third_number):
    return first_number + second_number + third_number

46. Развёрнутая запись функции

Создадим функцию, которая возвращает минимальное и максимальное числа:

def get_minimum_and_maximum(numbers):
    minimum_number = min(numbers)
    maximum_number = max(numbers)
    result = (minimum_number, maximum_number)
    return result

Здесь каждое действие записано отдельно.


47. Как работает развёрнутая функция

def get_minimum_and_maximum(numbers):
    minimum_number = min(numbers)
    maximum_number = max(numbers)
    result = (minimum_number, maximum_number)
    return result

Порядок работы:

  1. функция получает итерируемый объект;
  2. находит минимальное число;
  3. находит максимальное число;
  4. создаёт кортеж;
  5. возвращает кортеж.

48. Вызов развёрнутой функции

result = get_minimum_and_maximum([7, 10, 2, 5, 3])

print(result)

Результат:

(2, 10)

Первый элемент кортежа — минимальное значение.

Второй элемент — максимальное значение.


49. Распаковка результата

Функция возвращает кортеж с двумя элементами:

(2, 10)

Его можно распаковать:

minimum_number, maximum_number = get_minimum_and_maximum(
    [7, 10, 2, 5, 3]
)

print(minimum_number)
print(maximum_number)

Результат:

2
10

50. Работа со списком и кортежем

print(get_minimum_and_maximum([7, 10, 2, 5, 3]))

Результат:

(2, 10)
print(get_minimum_and_maximum((34, 142, 86)))

Результат:

(34, 142)

Функция работает с изученными итерируемыми объектами, элементы которых можно сравнить с помощью min() и max().


51. Сокращённая запись функции

Ту же функцию можно записать короче:

def get_minimum_and_maximum(numbers):
    return (min(numbers), max(numbers))

Результат будет таким же.


52. Сравнение двух вариантов

Развёрнутый вариант:

def get_minimum_and_maximum(numbers):
    minimum_number = min(numbers)
    maximum_number = max(numbers)
    result = (minimum_number, maximum_number)
    return result

Короткий вариант:

def get_minimum_and_maximum(numbers):
    return (min(numbers), max(numbers))

Оба варианта правильные.


53. Когда полезен развёрнутый вариант

Развёрнутая запись удобна, когда:

  • алгоритм только изучается;
  • нужно видеть каждый этап;
  • промежуточные значения имеют важный смысл;
  • результаты будут использоваться несколько раз;
  • отдельные имена делают код понятнее.

Пример:

minimum_number = min(numbers)
maximum_number = max(numbers)

По именам сразу понятно, что находится в переменных.


54. Когда полезен короткий вариант

Короткая запись подходит, когда:

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

Пример:

return (min(numbers), max(numbers))

Запись остаётся понятной и не содержит лишних имён.


55. Не нужно создавать переменную для каждого действия

Такой код допустим:

def calculate_square_area(side_length):
    result = side_length ** 2
    return result

Но переменная result используется только один раз.

Поэтому можно написать:

def calculate_square_area(side_length):
    return side_length ** 2

Второй вариант короче и остаётся понятным.


56. Когда промежуточную переменную стоит создать

Предположим, результат используется несколько раз:

def calculate_numbers_total(numbers):
    total = sum(numbers)

    print('Calculated total:', total)

    return total

Переменная total нужна и для print(), и для return.

В таком случае нет смысла дважды вызывать:

sum(numbers)

57. Повторное вычисление без переменной

Менее удачный вариант:

def calculate_numbers_total(numbers):
    print('Calculated total:', sum(numbers))
    return sum(numbers)

Функция два раза выполняет один и тот же расчёт.

Лучше вычислить результат один раз:

def calculate_numbers_total(numbers):
    total = sum(numbers)

    print('Calculated total:', total)

    return total

58. Читаемость важнее минимального количества строк

Самый короткий код не всегда является самым хорошим.

Главная задача — написать код, который:

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

Иногда промежуточная переменная улучшает код.

Иногда она только создаёт лишнее имя.


59. Функция является объектом

Когда Python выполняет:

def calculate_square_area(side_length):
    return side_length ** 2

в оперативной памяти появляется объект функции.

Имя:

calculate_square_area

связывается с этим объектом:

calculate_square_area ─────> объект функции

Имя функции является такой же ссылкой на объект, как и другие имена в Python.


60. Передача функции без вызова

Рассмотрим:

function_copy = calculate_square_area

Круглых скобок нет.

Поэтому функция не вызывается.

Имя function_copy начинает ссылаться на тот же объект функции:

calculate_square_area ──┐
                        ├────> объект функции
function_copy ──────────┘

61. Вызов функции через второе имя

function_copy = calculate_square_area

print(function_copy(5))

Результат:

25

Имя function_copy ссылается на тот же объект функции.

Поэтому через него тоже можно выполнить вызов.


62. Копирование ссылки и вызов — разные действия

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

function_copy = calculate_square_area

Вызов функции и сохранение результата:

result = calculate_square_area(5)

В первом случае в function_copy находится объект функции.

Во втором случае в result находится число:

25

63. Функция sys.getrefcount()

Для исследования количества ссылок можно использовать:

from sys import getrefcount

Создадим функцию:

def calculate_square_area(side_length):
    return side_length ** 2

Проверим счётчик:

print(getrefcount(calculate_square_area))

В CPython часто будет показано:

2

Одна ссылка связана с глобальным именем функции.

Ещё одна временная ссылка появляется при передаче объекта в саму функцию getrefcount(). Точное число может зависеть от версии Python и среды запуска, поэтому для основной логики программы на него полагаться нельзя.


64. Дополнительная ссылка на функцию

from sys import getrefcount


def calculate_square_area(side_length):
    return side_length ** 2


print(getrefcount(calculate_square_area))

function_copy = calculate_square_area

print(getrefcount(calculate_square_area))

Типичный результат:

2
3

После строки:

function_copy = calculate_square_area

появилась дополнительная ссылка на объект функции.


65. Удаление первоначального имени

del calculate_square_area

Первоначальное имя удалено.

Но объект функции продолжает существовать, потому что остаётся другая ссылка:

function_copy

Функцию всё ещё можно вызвать:

print(function_copy(5))

Результат:

25

66. Имя удалено, но объект остался

После:

del calculate_square_area

такой вызов больше не работает:

calculate_square_area(5)

Будет ошибка:

NameError

Но:

function_copy(5)

продолжает работать.

Удалено только имя calculate_square_area, а не сам объект функции.


67. Полный пример со ссылками на функцию

from sys import getrefcount


def calculate_square_area(side_length):
    return side_length ** 2


print(getrefcount(calculate_square_area))

function_copy = calculate_square_area

print(getrefcount(calculate_square_area))

del calculate_square_area

print(getrefcount(function_copy))
print(function_copy(5))

Типичный результат:

2
3
2
25

Точные значения счётчика могут отличаться.

Для нас важен общий принцип:

  • новое имя увеличивает количество ссылок;
  • удаление имени уменьшает количество ссылок;
  • объект существует, пока на него остаются ссылки.

68. Нужно ли копировать имя функции

Технически это возможно:

function_copy = calculate_square_area

Но в обычном коде без причины так делать не нужно.

У функции уже есть понятное имя:

calculate_square_area

Именно его следует использовать на протяжении программы.

Создание дополнительных случайных имён:

  • усложняет чтение;
  • может запутать других разработчиков;
  • затрудняет поиск функции;
  • не даёт практической пользы в простом коде.

69. Не нужно перезаписывать имя функции

Неправильная практика:

def calculate_square_area(side_length):
    return side_length ** 2


calculate_square_area = 10

После перезаписи имя больше не ссылается на функцию.

Вызов:

calculate_square_area(5)

приведёт к ошибке:

TypeError

Имя функции желательно сохранять неизменным.


70. Дублирование кода без функции

Предположим, в программе несколько раз рассчитывается периметр прямоугольника.

Без функции код может выглядеть так:

first_length = 5
first_width = 3
first_perimeter = (first_length + first_width) * 2

print('First perimeter:', first_perimeter)


second_length = 10
second_width = 4
second_perimeter = (second_length + second_width) * 2

print('Second perimeter:', second_perimeter)


third_length = 7.5
third_width = 2.5
third_perimeter = (third_length + third_width) * 2

print('Third perimeter:', third_perimeter)


fourth_length = 12
fourth_width = 8
fourth_perimeter = (fourth_length + fourth_width) * 2

print('Fourth perimeter:', fourth_perimeter)

Формула повторяется четыре раза:

(length + width) * 2

71. Недостатки повторяющегося кода

В предыдущем примере:

  • одна формула записана несколько раз;
  • файл стал длиннее;
  • увеличилась вероятность опечатки;
  • при изменении алгоритма придётся исправлять несколько мест;
  • отдельные расчёты сложнее проверять.

Например, в одной копии можно случайно написать:

length + width * 2

и получить неправильный результат.


72. Выносим алгоритм в функцию

Создадим функцию:

def calculate_rectangle_perimeter(length, width):
    return (length + width) * 2

Теперь формула находится только в одном месте.


73. Использование функции

def calculate_rectangle_perimeter(length, width):
    return (length + width) * 2


first_perimeter = calculate_rectangle_perimeter(5, 3)
second_perimeter = calculate_rectangle_perimeter(10, 4)
third_perimeter = calculate_rectangle_perimeter(7.5, 2.5)
fourth_perimeter = calculate_rectangle_perimeter(12, 8)

print('First perimeter:', first_perimeter)
print('Second perimeter:', second_perimeter)
print('Third perimeter:', third_perimeter)
print('Fourth perimeter:', fourth_perimeter)

Формула написана один раз, а функция вызвана четыре раза.


74. Результаты программы

First perimeter: 16
Second perimeter: 28
Third perimeter: 20.0
Fourth perimeter: 40

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


75. Сравнение двух подходов

Без функции:

first_perimeter = (first_length + first_width) * 2
second_perimeter = (second_length + second_width) * 2
third_perimeter = (third_length + third_width) * 2
fourth_perimeter = (fourth_length + fourth_width) * 2

С функцией:

def calculate_rectangle_perimeter(length, width):
    return (length + width) * 2

Дальше используются только вызовы:

calculate_rectangle_perimeter(5, 3)
calculate_rectangle_perimeter(10, 4)
calculate_rectangle_perimeter(7.5, 2.5)
calculate_rectangle_perimeter(12, 8)

76. Исправление алгоритма в одном месте

Предположим, в формуле функции была ошибка.

Без функции пришлось бы исправлять каждую копию формулы.

С функцией достаточно исправить одно место:

def calculate_rectangle_perimeter(length, width):
    return (length + width) * 2

Все вызовы автоматически начнут использовать исправленный алгоритм.


77. Понятное имя заменяет объяснение

Сравним:

result = (5 + 3) * 2

По этой строке нужно самостоятельно догадаться, что именно рассчитывается.

С функцией:

result = calculate_rectangle_perimeter(5, 3)

Название сразу объясняет назначение вычисления.


78. Принцип DRY

Один из известных принципов разработки называется:

DRY

Расшифровка:

Don't Repeat Yourself

Перевод:

Не повторяйся

Смысл принципа:

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


79. Функции помогают соблюдать DRY

Если один алгоритм нужен несколько раз, его можно поместить в функцию:

def calculate_rectangle_perimeter(length, width):
    return (length + width) * 2

После этого в программе вызывается готовая функция:

calculate_rectangle_perimeter(5, 3)
calculate_rectangle_perimeter(10, 4)

Алгоритм существует в одном месте.


80. DRY не означает объединять всё подряд

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

Функция должна представлять понятную самостоятельную задачу.

Хороший пример:

calculate_rectangle_perimeter()

Функция выполняет одну понятную операцию.

Плохая функция может объединять несколько несвязанных действий только ради уменьшения количества строк.


81. Основные термины урока

Параметр

Имя в определении функции:

def calculate_square_area(side_length):

side_length — параметр.

Аргумент

Значение при вызове:

calculate_square_area(5)

5 — аргумент.

Обязательный параметр

Параметр, для которого при вызове нужно передать значение.

Позиционный аргумент

Аргумент, который передаётся на определённой позиции:

add_numbers(10, 5)

82. Что изучено в трёх частях темы

В первой части мы познакомились с:

  • объектом None;
  • возвращаемыми значениями;
  • назначением функций;
  • устранением дублирования.

Во второй части мы изучили:

  • ключевое слово def;
  • тело функции;
  • отступы;
  • pass;
  • вызов функции;
  • return;
  • локальную область;
  • отличие print() от return.

В третьей части мы изучили:

  • параметры;
  • аргументы;
  • позиционную передачу;
  • обязательные параметры;
  • функции с одним, двумя и тремя параметрами;
  • локальные параметры;
  • ожидаемые типы данных;
  • функции как объекты;
  • ссылки на функции;
  • принцип DRY.

83. Что пока не рассматриваем

На этом этапе не изучаем:

  • передачу аргументов по имени;
  • значения параметров по умолчанию;
  • произвольное количество аргументов;
  • аннотации типов;
  • строки документации;
  • вложенные функции;
  • рекурсию;
  • декораторы;
  • генераторы.

Эти возможности будут изучаться постепенно в следующих темах.


84. Основные правила урока

Функция с одним параметром:

def calculate_square_area(side_length):
    return side_length ** 2

Вызов с одним аргументом:

calculate_square_area(5)

Функция с двумя параметрами:

def add_numbers(first_number, second_number):
    return first_number + second_number

Вызов с двумя аргументами:

add_numbers(10, 5)

Первый аргумент попадает в первый параметр.

Второй аргумент попадает во второй параметр.

Параметры являются локальными именами функции.

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

Подходящие типы данных определяются операциями внутри функции.

Результат можно вернуть напрямую:

return side_length ** 2

Или сначала сохранить:

area = side_length ** 2
return area

Функция является объектом, а её имя — ссылкой на этот объект.

Одинаковую логику лучше один раз поместить в функцию и затем переиспользовать:

DRY — Don't Repeat Yourself

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