- Что такое замыкания
- Когда возникают замыкания
- Scope
- Как работают замыкания
- Где применяются замыкания
- Выводы
Представим, что нам нужно передать значения между функциями, при этом нам не хочется создавать глобальные переменные или передавать их через параметры функции. В этом случае стоит использовать замыкания, которые мы разберем в этом уроке. Мы узнаем, что это такое, когда они возникают, как работать с ними и где они используются.
Что такое замыкания
Замыкания — это функция, которая запоминает значения из своей внешней области видимости, даже если эта область уже недоступна. Она создается, когда функция объявляется, и продолжает запоминать значения переменных даже после того, как вызывающая функция завершит свою работу.
Замыкания — это инструмент, который позволяет сохранять значения и состояние между вызовами функций, создавать функции на лету и возвращать их из других функций.
Когда возникают замыкания
Замыкание возникает, когда функция объявляется внутри другой функции и использует переменные из внешней функции. В этом случае внешняя функция создает замыкание, которое хранит ссылку на внешние переменные, используемые во внутренней функции. Замыкание позволяет внутренней функции получить доступ к этим переменным, даже если внешняя функция уже завершилась.
Рассмотрим пример кода, чтобы проиллюстрировать это понятие:
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
closure = outer_function(10)
print(closure(5)) # => 15
В этом примере мы создаем функцию outer_function
, которая принимает аргумент x
и возвращает внутреннюю функцию inner_function
. Внутренняя функция также принимает аргумент y
и возвращает сумму x
и y
.
Затем мы создаем замыкание closure
, вызывая outer_function
с аргументом 10
. Теперь closure
ссылается на inner_function
и хранит значение x
как 10.
В конце вызываем closure
с аргументом 5
и выводим результат — 15
. Замыкание closure
сохраняет значение x
как 10 между вызовами, поэтому оно может быть использовано внутри inner_function
даже после того, как outer_function
уже завершила свою работу.
Scope
Scope — это область видимости в Python, которая определяет доступность переменных внутри блока кода. Scope определяет, где переменные могут быть использованы, и какие имена переменных могут быть вызваны в каждой области кода.
В Python есть две области видимости переменных:
- Глобальная — относится к переменным, которые определены вне функций, классов или модулей. Если переменная определена в глобальной области видимости, она может быть использована в любом месте программы
- Локальная — относится к переменным, которые определены внутри функций, классов или методов. Если переменная определена в локальной области видимости функции, то она не может быть использована вне этой функции
Рассмотрим пример:
x = 10 # глобальная переменная
def my_func():
x = 5 # локальная переменная
print("x внутри функции:", x)
my_func()
print("x вне функции:", x)
В этом примере у нас две переменные с именем x
. Одна является глобальной переменной, определенной вне функции. А другая — локальная переменная, определенная внутри функции. Вывод этого кода будет следующим:
x внутри функции: 5
x вне функции: 10
Когда мы обращаемся к переменной внутри функции, Python ищет ее сначала в локальной области видимости функции, а затем — в глобальной области видимости. Если переменная не найдена ни в одной из этих областей, Python генерирует исключение NameError
.
Чтобы изменить значение глобальной переменной внутри функции, нужно явно объявить ее как глобальную с помощью ключевого слова global
:
x = 5
print(x) # => 5
def foo():
global x
x = 10
print(x)
foo() # => 10
print(x) # => 10
Здесь мы объявляем переменную x
как глобальную внутри функции foo
с помощью ключевого слова global
. После этого мы можем изменять ее значение, которое будет сохранено после выполнения функции.
nonlocal
— это ключевое слово в Python, которое используется при замыканиях во внутренней функции. Оно позволяет изменять значение переменных, определенных во внешней функции.
Например:
def outer():
x = 1
def inner():
nonlocal x
x = 2
print("outer:", x)
inner()
print("outer:", x)
outer()
В этом примере мы создали две функции:
outer
— имеет переменнуюx
со значением 1. Она выводит первоначальное значениеx
и затем вызывает функциюinner
. После вызоваinner
она выводит новое значениеx
.inner
— устанавливает значениеx
равному 2, используя ключевое словоnonlocal
.
При выполнении этого кода будет выведено:
outer: 1
outer: 2
Значение переменной x
изменяется в функции inner
с помощью ключевого слова nonlocal
и это изменение также отражается в функции outer
.
Как работают замыкания
Замыкание состоит из двух частей:
- Внешняя функция
- Внутренняя функция
Внутренняя функция имеет доступ к переменным из внешней функции даже после того, как внешняя функция завершила свою работу. Это происходит, так как замыкание сохраняет ссылку на эти переменные, а не копирует их значение. Так замыкания могут использовать и изменять значения этих переменных между вызовами.
Теперь рассмотрим несколько примеров замыканий:
def counter():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
c = counter()
print(c()) # 1
print(c()) # 2
print(c()) # 3
В этом примере мы создаем функцию counter
, которая возвращает внутреннюю функцию inner
. Внутри counter
мы определяем переменную count
и возвращаем inner
. Внутренняя функция inner
использует переменную count
, которая определена во внешней функции counter
с помощью оператора nonlocal
, и увеличивает ее значение на единицу при каждом вызове.
Затем мы создаем замыкание c
, вызывая counter
. Замыкание c
ссылается на inner
и хранит значение count
равное 0
.
В конце вызываем c
три раза и выводим результаты, которые должны быть 1
, 2
и 3
. Замыкание c
сохраняет значение count
между вызовами, поэтому переменная count
увеличивается на единицу каждый раз, когда мы вызываем c
.
Еще один пример:
def add_number(n):
def inner(x):
return x + n
return inner
add_five = add_number(5)
add_ten = add_number(10)
print(add_five(3)) # 8
print(add_ten(3)) # 13
В этом примере мы создаем функцию add_number
, которая принимает аргумент n
и возвращает внутреннюю функцию inner
. Внутренняя функция inner
также принимает аргумент x
и возвращает сумму n
и x
.
Затем мы создаем два замыкания: add_five
и add_ten
, вызывая add_number
с аргументами 5
и 10
соответственно.
В конце вызываем каждое замыкание с аргументом 3
и выводим результаты, которые должны быть 8
и 13
. Замыкания add_five
и add_ten
хранят значения n
как 5
и 10
между вызовами, поэтому они могут быть использованы внутри inner_function
даже после того, как add_number
уже завершила свою работу.
Где применяются замыкания
Замыкания также используются для создания функций с доступом к переменным, которые не должны быть изменены другими функциями. Это может быть полезно, например, при создании генераторов случайных чисел или при работе с конфигурационными файлами.
Рассмотрим пример такой функции:
def password_protected(password):
def inner():
if password == 'secret':
print("Access granted")
else:
print("Access denied")
return inner
login = password_protected('secret')
login() # Access granted
Здесь мы создаем функцию password_protected
, которая возвращает внутреннюю функцию inner
. Она проверяет, равен ли аргумент password
, переданный при создании password_protected
, 'secret'
, и выводит соответствующее сообщение.
Еще пример:
config = {
'language': 'ru',
'timezone': 'UTC'
}
def get_config(key):
def inner():
return config.get(key, None)
return inner
get_language = get_config('language')
get_timezone = get_config('timezone')
print(get_language()) # ru
print(get_timezone()) # UTC
Здесь мы создаем словарь config
и функцию get_config
. Эта функция возвращает внутреннюю функцию inner
, которая возвращает значение, связанное с переданным ключом. Затем мы создаем функции get_language
и get_timezone
с помощью get_config
, чтобы получить значения из словаря config
.
Выводы
Замыкания — это концепция функционального программирования, которая позволяет создавать функции с доступом к переменным, находящимся вне их области видимости. Они широко применяются в различных областях программирования, таких как работа с файлами, сетевыми протоколами и многопоточностью.
Основные преимущества замыканий в Python:
- Позволяют создавать функции, которые имеют доступ к переменным, находящимся вне их области видимости
- Позволяют создавать гибкие и эффективные решения для различных задач программирования
Основные недостатки замыканий:
- Могут привести к утечкам памяти, если они используются неправильно
- Могут замедлить производительность, если используется большое количество замыканий
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.