10.Функции
В предыдущих блоках мы использовали встроенные в Python функции print()
, input()
, int()
, str()
, len()
и многие другие. Пришло время начать писать свои собственные функции.
Разберём задачу: требуется изобразить звёздный прямоугольник 6 x 10 (6 строк, 10 столбцов). Самый простой вариант кода:
print('**********')
print('**********')
print('**********')
print('**********')
print('**********')
print('**********')
Воспользуемся циклами и умножением чисел на строки:
for _ in range(6):
Вывод (в обоих примерах):
print('*' * 10)
**********
**********
**********
**********
**********
**********
Так код выглядит гораздо лучше. Теперь предположим, что нам необходимо вывести 3 таких прямоугольника, тогда код будет выглядеть так:
for _ in range(6):
Вывод:
print('*' * 10)
print()
for _ in range(6):
print('*' * 10)
print()
for _ in range(6):
print('*' * 10)
print()
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
**********
И хотя предыдущий код полностью решает поставленную задачу, он не лишен недостатков. Во-первых, он довольно громоздкий из-за повторения части кода, отвечающей за вывод прямоугольника. Во-вторых, если понадобится изменить размеры прямоугольника, придется менять их трижды, в каждой части кода, выводящей прямоугольник.
Вместо повторения кода для вывода прямоугольника, можно перенести его в отдельную функцию и вызвать ее 3 раза.
Для создания функции пишем такой код:
def draw_box():
for _ in range(6):
print('*' * 10)
Когда функция создана, чтобы увидеть результат ее работы, надо вызвать ее по имени:
draw_box()
Теперь, чтобы изобразить 3 прямоугольника, можно написать код:
draw_box()
print()
draw_box()
print()
draw_box()
Код стал короче, читабельнее (за счет удачного названия функции), а главное, если потребуются иные размеры прямоугольника,
достаточно будет изменить только саму функцию draw_box()
.
Для объявления (на англ. define) функции нужно прописать следующее:
def
[название_функции]():
блок кода
В примере выше название функции - draw_box, блок кода:
for _ in range(6):
print('*' * 10)
Для вызова функции достаточно прописать её название с круглыми скобками, например, draw_box()
Нельзя вызвать функцию до её объявления:
draw_box()
Этот код неверен и вызовет ошибку.
def draw_box():
for _ in range(6):
print('*' * 10)
Если необходимо, чтобы функция ничего не выполняла, то следует в блоке кода указать pass:
def func():
pass
Параметры
Функции могут принимать параметры в скобках и работать с ними:
def sqrt(n):
Вывод:
print(n ** 0.5)
sqrt(16)
sqrt(n=16)
4.0
1.4142135623730951
В данном примере в функцию может быть передан аргумент, который в блоке будет использовать значение в качестве переменной n.
При вызове мы можем неявно передать аргумент: sqrt(16)
, а можем явно указать в качестве какого параметра мы передаём аргумент: sqrt(n=2)
.
Также мы можем передать более одного параметра:
def sqrt(a, b):
Вывод:
10
7
print(a + b)
sm(3, 7)
sm(a=2, b=5)
Если мы по имени параметра передаём аргументы, то необходимо сначала передать аргументы неявно, после по имени, а также если указали первый аргумент неявно,
то нельзя передать потом его через имя параметра:
sm(2, b=3)
Вывод:
5
sm(a=2, 3)
Вывод:
SyntaxError: positional argument follows keyword argument
sm(2, a=5)
Вывод:
TypeError: sm() got multiple values for argument 'a'
Функция с возвратом значения
Во всех предыдущих примерах мы печатали через print все значения из функции на экран, но если нам необходимо потом использовать значения из функции, то мы можем вернуть его. Если функция возвращает какое-то значение, то она завершается. Когда функция с возвратом значения завершается, она возвращает значение в ту часть программы, которая ее вызвала. Возвращаемое из функции значение используется как любое другое: оно может быть присвоено переменной, выведено на экран, использовано в математическом выражении (если это число) и т. д.
Мы уже сталкивались со многими функциями с возвратом значений:
- функция
int()
– преобразует строку к целому числу и возвращает его; - функция
range()
– возвращает последовательность целых чисел 0, 1, 2, ...; - функция
abs()
– возвращает абсолютное значение числа (модуль числа).
Функцию с возвратом значения пишут точно так же, как и без, но она должна иметь инструкцию return.
Вот общий формат определения функции с возвратом значения в Python:
def
[название_функции]():
блок кода
return выражение
В функции должна быть инструкция return, принимающая форму: return выражение
Значение выражения, которое следует за ключевым словом return, будет отправлено в ту часть программы, которая вызвала функцию. Это может быть переменная либо выражение, к примеру, математическое.
Напишем функцию, которая будет переводить градусы по шкале Фаренгейта в градусы по шкале Цельсия по формуле C = 5/9 * (F - 32):
def convert_to_celsius(temp):
Задача этой функции — принять одно число temp в качестве аргумента – количество градусов по шкале Фаренгейта, и вернуть другое — количество градусов по шкале Цельсия.
result = (5 / 9) * (temp - 32)
return result
Рассмотрим ее работу. Первая инструкция в блоке функции присваивает значение (5 / 9) * (temp - 32) переменной result.
Затем исполняется инструкция return, которая приводит к завершению исполнения функции и отправляет значение из переменной result, назад в ту часть программы, которая вызвала эту функцию:
# функция перевода градусов Фаренгейта в градусы Цельсия
def convert_to_celsius(temp):
result = (5 / 9) * (temp - 32)
return result
# основная программа
temp = float(input('Bвeдитe количество градусов по Фаренгейту: '))
celsius = convert_to_celsius(temp)
print(celsius) # градусы Цельсия
Основная программа получает от пользователя одно число – значение в градусах Фаренгейта, и вызывает функцию, передавая значение переменной temp в качестве аргумента. Значение, которое возвращается из функции convert_to_celsius, присваивается переменной celsius.
Взглянем еще раз на функцию convert_to_celsius()
:
def convert_to_celsius(temp):
result = (5 / 9) * (temp - 32)
return result
Обратите внимание, что внутри этой функции происходят две вещи: во-первых, переменной result присваивается значение выражения (5 / 9) * (temp - 32), и во-вторых,
значение переменной result возвращается из функции. Эта функция хорошо справляется с поставленной перед ней задачей, но ее можно упростить.
Поскольку инструкция return возвращает значение выражения, переменную result устраняем и переписываем функцию так:
def convert_to_celsius(temp):
return (5 / 9) * (temp - 32)
Эта версия функции не сохраняет значение (5 / 9) * (temp - 32) в отдельной переменной, а сразу возвращает значение выражения с помощью инструкции return. Делает то же, что и предыдущая версия, но за один шаг.
Распаковка последовательностей
В Python есть возможность из какой-либо последовательности внести значения в переменные, например:
l = [3,1.12,"st"]
Вывод:
a, b, c = l
print(a)
print(b)
print(c)
3
1.12
st
Но очень важно то, чтобы количество переменных совпадало с количеством значений последовательности, иначе произойдёт ошибка:
a, b = range(3)
Вывод:
print(a, b)
ValueError: too many values to unpack (expected 2)
Такое применимо ко всем функциям, в том числе к самописным:
def plus_one(numbers):
Вывод:
for i in range(len(numbers)):
numbers[i] += 1
return numbers
a, b, c = plus_one([1,7,5])
print(a)
print(b)
print(c)
2
8
6
Функция plus_one принимает в качестве аргумента список, в котором все значения увеличиваются на единицу, список возвращается, после значение данного списка распаковывается в 3 переменные.
Рекурсия
Функция может внутри своего блока кода вызывать себя, это явление называется рекурсия. У рекурсии есть некоторая схожесть с циклом while, если не будет условия остановки цикла/рекурсии, то процесс будет идти бесконечно.
Вот классический пример рекурсии, вычисление n-го числа Фибоначчи (https://ru.wikipedia.org/wiki/Числа_Фибоначчи):
def fibonacci(n):
Вывод:
if n == 1:
return 0
elif n == 2:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
34
Допустим, n = 4. Тогда произойдет рекурсивный вызов fibonacci(3)
и fibonacci(2)
. Второй вернет единицу, а первый приведет к еще двум вызовам функции: fibonacci(2)
и fibonacci(1)
.
Вызовы вернут один и ноль, в сумме будет один. Таким образом, вызов fibonacci(3)
возвращает число 1, которое суммируется с числом 1 от вызова fibonacci(2)
.
Результат 2 возвращается в основную ветку программы. Четвертый элемент ряда Фибоначчи равен двум: 0 1 1 2.
Изменение глубины рекурсии
При достаточно большом количестве вложенных вызовов рекурсивной функции может произойти ошибка из-за того, что по умолчанию глубина рекурсии равна 1000.
Это можно исправить с помощью функции setrecursionlimit из библиотеки sys:
from sys import setrecursionlimit
Обратите внимание: максимально возможное значение глубины рекурсии зависит от операционной системы. Поэтому бесконечно его увеличивать не получится.
setrecursionlimit(2000) # Установка нового предела глубины рекурсии