Функции служат для того, чтобы использовать код повторно, не переписывая его каждый раз. Давайте рассмотрим синтаксис функций:
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.
В этой статье описано, что означают различные классы исключений. Также рассмотрено создание собственных классов исключений.