т. (383) 381-86-26

Блог о создании вебсайтов

 

Замыкания в Python. Что это и с чем их едят.

 

Периодически в разных источниках всплывают вот такие вот куски кода:

 
def outer_func(x):
    def inner_func(y):
        # inner_func замкнуло в себе х
        return y + x
    return inner_func
 

Что это? Обычно такое определение функции inner_func называют замыканием (closure) - мы определили функцию внутри другой (внешней) функции замкнув её относительно внешней функции.

И зачем они нужны? Для чего сгодятся замыкания?

Всё выглядит круто, но опять же не понятно:"шо це такэ" оО. Выглядит как определение декоратора, возвращает функцию. Не-е-е понятно. Как мне кажется, путаница создаётся из-за общепринятого восприятия замыканий в отрыве от их истоков. Если обратиться к великолепной книге "Структура и интерпретация компьютерных программ", то найдём там очень простое определение замыкания:

В общем случае, операция комбинирования объектов данных обладает свойством замыкания в том случае, если результаты соединения объектов с помощью этой операции сами могут соединяться этой же операцией

Если же обратится к википедии, то редакторы статьи о замыканиях дают такое определение: "Замыкание (англ. closure) в программировании — функция, в теле которой присутствуют ссылки на переменные, объявленные вне тела этой функции в окружающем коде и не являющиеся её параметрами. Говоря другим языком, замыкание — функция, которая ссылается на свободные переменные в своём контексте" — это частный случай приведенного выше определения.

Теперь уже код выше не должен вызывать никаких нареканий. Он четко подходит под определение. А заодно из этого определения становится ясно, что понятие "замыкания" значительно шире. К примеру, список. Элементы списка также могут или являются списками — это замыкание в смысле SICP. Только в питонячей среде не принято на это внимание обращать, да и особого смысла нет. Методы объектов также являются замыканиями. Они замкнуты относительно объекта self, который всегда передаётся первым аргументом.

Применение замыканий

С применением замыкания в Python вы встречаетесь каждый день. Это и методы, и декораторы функций и классов, списки, кортежи и т.д. Рассмотрим пример замыкания в виде декоратора, проверяющего каким HTTP методом было совершено обращение к view в Django фреймворке. Работает он так: проверяем метод, если метод разрешен, то отрабатываем view, если не разрешен выбрасываем исключение:

 
def http_methods(methods=[]):
    """
        Данный декоратор проверяет метод HTTP запроса,
        который пришел на вью, перед тем как 
        она будет вызвана. 

        Удобен тем, что можно задать набор HTTP слов,
        на которые вьюха будет реагировать. В противном случае Http404.
    """
    def decorator(func):
        def wrapper(request, *args, **kwargs):
            if request.method in methods:
                view = func(request, *args, **kwargs)
                return view
            raise Http404
        return wrapper
    return decorator

#...
@http_methods(methods=['POST', 'GET'])
def your_view(request, *args, **kwargs):
    pass
 

И напоследок

Есть один нюанс. В Питоне замкнутые переменные доступны только для чтения. Чтобы обойти такое ограничение, нужно замыкать переменные контейнерного типа: списки, кортежи и т.д.. Сами замкнутые переменные нельзя будет перезаписывать, а вот содержимое контейнера - пожалуйста.

Сильно греть голову о замыканиях не следует. Разработчики языка обо всём уже подумали. В частности, сделали их применение максимально прозрачным для программистов. Но знание сакральных особенностей поможет вам ощущать себя лучше и глубже понимать поведение вашего python-кода.

Следите за нашими публикациями и вы узнаете ещё больше о сакральных особенностях Python, которые сделают из вас выдающегося программиста на языке Python! ^__~

Подпишитесь на рассылку, будет интересно!