Функции служат для того, чтобы использовать код повторно, не переписывая его каждый раз. Давайте рассмотрим синтаксис функций:
def имя (аргумент1, аргумент2, . . . аргументn):
. . .
return значение
Например, вот так может выглядеть функция:
def double(number):
return number * 2
затем мы можем использовать эту функцию:
double(9) #18
x = 33
double(x) #66
Как видно, мы можем передавать в функцию переменные. При этом, после передачи в функцию, переменная будет доступна для использования. Вот так мог бы выглядеть код выше, если бы мы не использовали функции:
9 * 2
x = 33
x * 2
В этом случае мы не сильно усложнили код, но в реальных ситуациях функции могут достигать длину в несколько десятков строк кода. Представьте, что было бы если бы мы каждый раз писали одинаковый код, вместо вызова одной функции.
Строго говоря, рекурсия - это когда функция вызывает сама себя. Но это не просто бессмысленный вызов самой себя, это - сведение задачи к базовому случаю. Например, напишем код рекурсивного вычисления факториала:
def factorial(n):
if n == 1:
return 1 # базовый случай
else:
return n * factorial(n-1)
Как работает код:
Функция factorial(n)
принимает один аргумент n
, который представляет число, для которого нужно вычислить факториал.
В первой строке условие if n == 1:
проверяет базовый случай, когда n
равно 1. В этом случае функция возвращает 1, так как факториал 1 равен 1.
Если n
не равно 1, то выполняется блок else:
, где происходит рекурсивный вызов функции factorial(n-1)
. Это означает, что функция будет вызывать саму себя с аргументом n-1
, пока не достигнет базового случая.
На каждом уровне рекурсии функция умножает текущее значение n
на результат вызова factorial(n-1)
, что в конечном итоге приводит к вычислению факториала исходного числа n
.
Если вызвать factorial(5)
, то процесс будет следующим:
- factorial(5)
вернет 5 * factorial(4)
- factorial(4)
вернет 4 * factorial(3)
- factorial(3)
вернет 3 * factorial(2)
- factorial(2)
вернет 2 * factorial(1)
- factorial(1)
вернет 1
- Затем произойдет обратное раскручивание рекурсии, где каждое значение будет умножаться на предыдущее, в итоге получится 5! = 120.
Давайте посмотрим, что будет если попытаться обратиться к внешней переменной из функции:
count_of_calls = 0
def double_and_count(number):
count_of_calls += 1
return number * 2
Если вызвать такую функцию, будет получена ошибка:
UnboundLocalError: local variable 'count_of_calls' referenced before assignment on line 4 in main.py
Эта ошибка говорит нам о том, что мы ссылаемся на переменную, которая не объявлена. Но почему, мы же объявили ее count_of_calls = 0
! Объявили, но в глобальной области видимости. В Python существует три области видимости - локальная, глобальная, и нелокальная
Давайте рассмотрим области видимости на примере нашего кода.
count_of_calls = 0 # Уровень вложенности 0, глобальная область видимости
def double_and_count(number):
count_of_calls += 1 # Уровень вложенности 1, локальная область видимости
return number * 2 # то же самое
Как мы видим, все что на уровне вложенности 0 - глобальная область видимости, все что внутри функций - уровень вложенности 1 - локальная область видимости, а если переменная находится между уровнем 0 и уровнем вызывающего кода, то она является нелокальной. например, если вызывающий код находится на уровне 2, то уровень 1 будет для него нелокальной областью видимости.
Чтобы получить из уровня 1 доступ к переменным уровня 0, нужно воспользоваться ключевым словом global
перед вызовом этой переменной:
count_of_calls = 0
def double_and_count(number):
global count_of_calls
count_of_calls += 1
return number * 2
Выражение global count_of_calls
означает, что теперь все вызовы count_of_calls
будут обращаться именно к той переменной из глобальной области видимости.
А чтобы получить доступ из уровня n к переменной из уровня n - 1, где n > 0, нужно использовать выражение nonlocal
def make_averager():
count = 0
total = 0
def averager(new_value):
nonlocal count, total
count += 1
total += new_value
return total / count
return averager
avg = make_averager()
print(avg(10))
print(avg(11))
print(avg(15))
Этот код определяет функцию make_averager()
, которая возвращает другую функцию averager()
.
make_averager()
использует замыкание, чтобы сохранить значения переменных count
и total
между вызовами функции averager()
.
Когда вы вызываете make_averager()
, создается новая функция averager()
, которая может обновлять значения count
и total
и вычислять среднее значение.
Сама функция averager()
принимает аргумент new_value
, добавляет его к текущему значению total
, увеличивает count
на 1 и возвращает текущее среднее значение (total
/count
).
Вызовы avg(10)
, avg(11)
и avg(15)
последовательно обновляют значения count
и total
и вычисляют среднее значение. Результаты этих вызовов будут выводиться на экран.
Например, после первого вызова avg(10)
значение total
станет равным 10
, а значение count
станет равным 1
. Следовательно, среднее значение будет равно 10
(10
/1
).
После второго вызова avg(11)
значение total
станет равным 21
(10
+ 11
), а значение count
станет равным 2
(1
+ 1
).
Следовательно, среднее значение будет равно 10.5
(21
/2
).После третьего вызова avg(15)
значение total
станет равным 36
(21
+ 15
), а значение count
станет равным 3
(2
+ 1
). Следовательно, среднее значение будет равно 12
(36
/3
).
Объектно-ориентированное программирование - одна из самых распространенных парадигм. Знание ООП позволит сделать код более гибким и масштабируемым
Для написания сложных программ необходимо использовать сторонние библиотеки. Они позволяют использовать готовый функционал. Также можно использовать модули для компоновки вашего приложения.
В Python 3.10 был добавлен новый функционал - сопоставление с шаблонами с помощью ключевых слов match и case. В статье также рассматривается производительность match/case по сравнению с if/else.
В этой статье описано, что означают различные классы исключений. Также рассмотрено создание собственных классов исключений.