AndreyMelnikov.MyBlog # мой блог: IT-марафон.

Все мои посты

Вселенная программирования. Ключевые концепции ч2 - Замыкания.

Серия тем о вселенной программирования продолжается. В предыдущей своей статье я описывал основные концепции программирования - то, что является фундаментальной идейной базой, то, на чем основывается искусство программирования. Данные идеи практически реализованы в языках программирования в различных комбинациях. И в той же статье рассказал об одной из четырех ключевых концепций - концепции именованного состояния.

Итак, сегодня речь пойдет о еще одной базовой концептуальной вещи в программировании - замыкания, а если быть точнее, замыкания с лексической областью видимости (lexically scoped closure). Это достаточно мощная концепция сама по себе. Например, функциональное программирование, по сути, и есть воплощение концепции замыкания. На данном этапе понятно словесно описать суть данной идеи достаточно тяжело, поэтому я для начала приведу пример кода с пояснениями.
Самой наглядной и популярной реализацией замыкания являются классические функции.

main_func, которая возвращает inner_func, которая обращается к локальной переменной name, определённой внутри main_func.
Когда вызвана main_func, она возвращает inner_func, которая запоминается где-то во внешней переменной (например, f = main_func()), после чего main_func как бы прекращает своё существование. Однако inner_func , если её теперь вызывать через переменную f(), должна использовать внутри себя переменную name, принадлежащую функции main_func, которая уже не существует.

Замыкание - это функция inner_func, которая связана со своим контекстом выполнения. Она замкнула в себе определённую информацию из внешней среды в пределах своей области видимости, и, хотя main_func формально не существует, inner_func будет корректно использовать переменную name, локальную внутри конкретного экземпляра функции inner_func.

Вот еще пример функции счетчика, реализованной при помощи замыкания:

def counter():
    count = 0
    def inner():
        nonlocal count
        count += 1
        return count
    return inner

c = counter()
for i in range(5):
    print(c())

# 1
# 2
# 3
# 4
# 5

r = counter()
for i in range(3):
    print(r())
# 1
# 2
# 3

Каждый раз при вызове функции counter(), она будет считать и запоминать кол-во своих вызовов. Если создать новый объект (при этом пространство имен обновится), то счетчик сбрасывается и считает заново.

Ну а теперь попробую описать суть концепции более простыми словами.
Итак, замыкание – это функция, связанная со своим контекстом выполнения использованием пространства имен. Главная идея тут заключается в разделении определения программной логики от ее исполнения через создание функций, которые возвращают своим результатом другие функции со своим контекстом. Другими словами, программа может взять некоторые инструкции кода в одном месте в виде отложенного “пакета работы”, передать их в другое место, и исполнить уже там. Причём результат выполнения пакета в новом месте программы будет точно таким же, как если бы этот пакет выполнялся там, где замыкание было исходно создано.

Кроме того, объекты и другие программные компоненты, хранящие внутри себя нужные состояния (или ссылки на них) – это также является замыканиями. Например, в ООП класс – это, по сути, функция, которая при своем вызове (через конструктор) возвращает объект – это тоже замыкание.
Таким образом, мы видим в концепции замыкания общую ключевую конструкцию для различных парадигм программирования, таких как функциональное, императивное и объектно-ориентированное. В большинстве языков программирования замыкания скрыты внутри имплементации языка и не доступны программисту напрямую.

Назад к списку постов