Вызов функции из функции
Если Вам необходимо вызвать функцию test2 из глобальной области видимости, то это невозможно.
Она является локальным объектом функции test1 и доступна только в контексте исполнения этой функции.
Если же Вы хотите вызвать её в теле функции test1 , то это тривиально.
Пример:
Функцию test2 можно вызвать, если ее вернуть из первой функции. Типичный способ применения — создание «замыкания» (closure): test2 будет помнить состояние локальных переменных (в том числе аргументов) функции test1 .
![]()
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.3.11.43304
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Функции и методы в Python: передача функции в функцию. Декоратор
Эта статья посвящена теме декораторов в Python. Поговорим о том, что это такое, уделим особое внимание свойствам функций в Python, на базе которых реализована данная идея, а также рассмотрим декораторы, которые принимают аргументы и возвращают значение из функции.
Что надо знать о методах и функциях в Python?
Говоря о функциях в Python, нужно упомянуть два аспекта: 1) функция в Python — есть объект специального вида, который можно передавать в виде аргумента другим функциям; 2) внутри функций в Python вы можете создавать другие функции, а также вызывать их, возвращая результат посредством return.
Теперь давайте поговорим об этом подробнее.
Функция как объект в Python
В языке программирования Python часто практикуется передача одной функции в виде аргумента другой функции. Представьте, что есть список целых чисел, и вы желаете на его базе получить другой список с элементами, которые будут квадратами первого списка. Вот, как это можно реализовать в Python:
В нашем примере мы передали функции map в виде первого аргумента функцию sq. Последняя будет по очереди применяться ко всем элементам нашего списка a.
Кроме того, в Python функция является специальным объектом, имеющим метод __call__() . Представьте, что мы создали следующий класс:
Объект такого класса в Python мы сможем вызывать как функцию:
Функция внутри функции в Python
Функции в Python мы можем создавать, вызывать и возвращать из других функций. Кстати, на этом основана идея замыкания (closures) в Python.
Давайте создадим функцию, умножающую 2 числа:
В этой функции в Python реализованы два важных свойства: 1) внутри функции mul() мы создаём ещё одну функцию helper() ; 2) функция mul() возвращает нам функцию helper() в качестве результата работы.
Вызов этой функции в Python:
Особенность заключается в том, что мы можем создавать на базе функции mul() собственные кастомизированные функции. Давайте создадим функцию в Python, умножающую на 3:
В результате была построена функция three_mul() , умножающая на 3 любое переданное ей число.
Декоратор функции в Python
Конструктивно речь идёт о некоторой функции, в качестве аргумента которого выступает другая функция. Декоратор в Python добавляет дополнительный функционал к функции, не меняя её содержимое.
Создание
Представьте, что мы имеем пару простых функций в Python:
При этом мы желаем их дополнить таким образом, чтобы перед вызовом основного кода нашей функции печаталась строчка “Start function”, а в конце – строка “Stop function”.
Реализовать поставленную задачу можно несколькими методами. Во-первых, мы можем добавить необходимые строки в конец и начало каждой функции. Но вряд ли это удобно, ведь если мы пожелаем их убрать, придётся модифицировать тело функции.
Теперь поговорим о втором пути. Для начала создадим функцию:
Теперь нужно обернуть функции в оболочку:
Обратите внимание, что функции first_test и second_test не поменялись.
Наши функции second_test_wrapped и first_test_wrapped обладают функционалом, который нам и нужен.
Теперь, если надо, чтобы так работали функции с именами first_test и second_test, делаем следующее:
Выполненные нами действия и являются реализацией идеи декоратора.
Правда, вместо строк:
мы можем написать иначе:
В нашем случае @simple_decore – это не что иное, как декоратор функции.
Передаём аргументы в функцию с помощью декоратора
Бывает, что функция требует наличие аргумента, поэтому мы можем передать его через декоратор. Давайте создадим декоратор, принимающий аргумент и выводящий информацию о декорируемой нами функции и её аргументе.
Чтобы продемонстрировать работу, создадим функцию, выводящую квадратный корень переданного ей числа, а в качестве декоратора, укажем созданный param_transfer:
Теперь давайте выполним данную функцию с аргументом 4:
Декораторы для методов класса в Python
С декоратором можно объявлять и методы классов. Давайте выполним модификацию декоратора param_transfer:
Теперь приступим к созданию класса для представления 2-мерного вектора (математического). В классе определим метод norm() , выводящий модуль вектора.
Что же, осталось продемонстрировать работу нашего метода:
Возвращаем результат работы функции через декоратор
Зачастую создаваемые функции выполняют возвращение какого-либо значения. Чтобы это стало возможным осуществить через декоратор, нужно специальным образом построить нашу внутреннюю функцию:
Возможно применение этого декоратора и для оборачивания функций, принимающих различные аргументы и возвращающие значение:
Замыкания в Python
Python позволяет определять функции внутри других функций. «Внутренняя» функция называется вложенной. Вот пример:
В этом примере мы объявили функцию display() внутри функции say() . Функция display() — вложенная.
При этом в функции display() используется переменная greeting , которая инициализирована вне этой функции, то есть из нелокальной области видимости.
В Python такие переменные как greeting называются свободными переменными.
На самом деле функция display() состоит из двух частей:
- Сама функция display() .
- Переменная greeting , в которой содержится значение «Привет» .
Так вот все вместе это называется замыканием (closure).
Замыкание (closure) — это вложенная функция, которая ссылается на одну или более переменных из объемлющей (enclosing) области видимости.
Можно вернуть функцию из функции
В Python функция может возвращать другую функцию. Например, так:
В этом примере функция say() возвращает функцию display() , а не выполняет ее.
Когда say() возвращает display() , на самом деле возвращается замыкание:
Следующая инструкция присваивает возвращаемое значение функции say() переменной fn . Поскольку fn — это функция, ее можно вызвать:
Функция say() выполняется и возвращает функцию fn() . Когда выполняется fn() , функция say() уже завершила выполнение.
Это значит, что область видимости функции say() уже не существует к тому моменту, когда выполняется fn() .
Поскольку переменная greeting принадлежит области видимости функции say() , она, по идеи, тоже должна уничтожаться после выполнения say() .
Однако fn() все равно как-то выводит значение переменной greeting , как вы видите в примере выше.
Разберемся, как это работает и почему так происходит.
Ячейки и переменные с несколькими областями видимости
Переменная greeting «разделяется» между двумя областями видимости:
- Функции say() .
- Замыкания.
То есть greeting находится одновременно в двух областях видимости. Тем не менее, она всегда ссылается на один и тот же строковый объект «Привет» .
Для этого Python создает промежуточный объект — ячейку (cell).
Узнать адрес ячейки в памяти можно через свойство __closure__ , как показано ниже:
Вывод
__closure__ возвращает кортеж ячеек.
В этом примере адрес ячейки в памяти — 0x0000017184915C40 . Она ссылается на строковый объект по адресу 0x0000017186A829B0 .
Если вы отобразите адрес памяти строкового объекта в функции say() и замыкании, то увидите, что они ссылаются на один и тот же объект в памяти:
Вывод
Когда вы обращаетесь к значению переменной greeting , Python технически дважды «прыгает» в память, чтобы получить значение строки.
Это объясняет, почему когда область видимости функции say() уже не существовала, вы все равно могли обратиться к строковому объекту, на который ссылается переменная greeting .
На основе этого механизма можно определить замыкание по-другому:
Замыкание — это функция и расширенная область видимости, в которой содержатся свободные переменные.
Чтобы узнать, какие свободные переменные содержатся в замыкание, можно использовать __code__.co_freevars . Например:
В этом примере fn.__code__.co_freevars вернет переменную greeting — единственную свободную переменную в замыкании fn .
Когда Python сам создает замыкания
Python создает новую область видимости каждый раз, когда выполняется функция. Если функция создает замыкание, Python тоже создаст новое замыкание. Рассмотрите следующий пример:
1. Сначала определим функцию multiplier() , которая возвращает замыкание:
Функция multiplier() возвращает произведение двух аргументов. Но возвращается не само произведение, а замыкание.
2. Теперь вызовем функцию multiplier() три раза:
Вызовы функция создадут три замыкания. Каждая функция умножает число на 1, 2, и 3 соответсвенно.
3. А теперь выполним функции замыканий:
Вывод
Как вы видите, у m1 , m2 и m3 разные инстансы замыкания.
Замыкания и цикл for
Предположим, что нам нужно создать все три вышеуказанные замыкания одновременно. Это можно сделать, например, так:
Что мы здесь делаем
- Объявляем список, в котором будем хранить замыкания.
- Используем лямбда-выражение, чтобы создать замыкание, и добавляем его в список на каждой итерации.
- «Распоковываем» замыкания из списка и присваиваем их переменным m1 , m2 и m3 соответсвенно.
- Передаем значения 10, 20 и 30 в каждое замыкание и выполняем их.
Вывод
Работает не так, как мы ожидали. Почему?
В цикле for x меняет значения от 1 до 3. Когда цикл завершается, значение x — 3.
Каждый элемент списка — соответсвующее замыкание lambda y: x*y .
Python использует значение x для вычислений каждый раз, когда вы вызываются m1(1) , m2(10) и m3(10) . И поскольку в момент выполнения замыкания x всегда 3, результат всех замыканий одинаковый — 30.
Чтобы исправить эту проблему, нужно объяснить Python, что значение x для вычислений нужно использовать в цикле.
О декораторах в Python
Перевод статьи подготовлен для студентов курса «Web-разработчик на Python». Интересно развиваться в данном направлении? Запишитесь на День Открытых Дверей курса и пообщайтесь вживую с преподавателем: онлайн-трансляция 23 июля в 20:00 по мск.!

Когда вы упражнялись в программировании на языке Python, вы, должно быть сталкивались с таким понятием, как декораторы. Они являются одним из самых элегантных и часто используемых инструментов в современных библиотеках и фреймворках. Декораторы — хороший способ инкапсулировать множество деталей реализации, оставляя на поверхности простой интерфейс.
Давайте рассмотрим пример простого декоратора логина, который проверяет, что пользователь вошел в систему, прежде чем давать ему возможности редактировать посты. Потом декоратор перенаправляет на страницу входа в систему или регистрации, а затем с помощью правильно заданных параметров возвращает обратно на ту же страницу после успешного прохождения идентификации. Чтобы воспользоваться данной функцией, вам нужно всего лишь написать @login_required перед целевой функцией.
С декораторами очень легко работать, но создание декораторов задача непростая даже для опытных Python-программистов. В этой статье мы с вами рассмотрим пошагово как работают декораторы в Python.
Функции
Функции также называются объектами первого класса в Python. Функции это такие же значения, как числа, списки и строки, как видно из следующего примера.
У функций есть свое собственное пространство имен, где происходит поиск имен переменных в первую очередь, когда они встречаются в теле функции. Давайте напишем простую функцию, чтобы понять разницу между глобальной и локальной областью видимостью.
Область видимости функции как переменная
Правило области видимости в Python гласит, что при создании переменной всегда создается новая локальная переменная, но доступ к переменной определяется в локальной области видимости при поиске по всем ближайшим областям совпадений имен переменных. Это не значит, что мы не можем получить доступ к глобальным переменным из нашей функции. Чтобы выводить глобальную переменную мы изменим функцию foo следующим образом:
Время жизни переменной
В пространстве имен живут не только переменные, но и они имеют время жизни, о котором важно помнить. Рассмотрим пример, который иллюстрирует не только правила области видимости и те проблемы, которые они могут вызвать, но и то, как они взаимодействуют с вызовом функции и то как они работают в Python и других языках.
Вложенные функции
В Python можно создавать вложенные функции, а это значит, что мы можем объявлять функции внутри функций, и все правила области видимости и времени жизни по-прежнему действуют.
Декораторы
Замыкание (closure), которое принимает функцию в качестве параметра и возвращает функцию, называется декоратором. Рассмотрим пример полезных декораторов.
Декорированная переменная представляет собой декорированную версию foo. На самом деле, мы могли бы заменить foo ее декорированной версией и не изучать новый синтаксис, просто переназначив переменную, содержащую нашу функцию:
Теперь для отслеживания вызовов функций у нас есть красивый декоратор. Декораторы могут использоваться для работы с любым языком программирования с помощью Python. Это крайне полезный инструмент, механизм работы которого необходимо понять, чтобы правильно их применять.