- Замыкания в Python
- Можно вернуть функцию из функции
- Ячейки и переменные с несколькими областями видимости
- Когда Python сам создает замыкания
- Замыкания и цикл for
- Что мы здесь делаем
- Как вызвать функцию в Python?
- Создание функции в Python
- Как создать и использовать вложенные функции в Python
- Что такое вложенные функции?
- Создание вложенных функций
- Использование вложенных функций
- Пример использования вложенных функций для реализации декораторов
Замыкания в Python
Python позволяет определять функции внутри других функций. «Внутренняя» функция называется вложенной. Вот пример:
def say(): greeting = "Привет" def display(): print(greeting) display()
В этом примере мы объявили функцию display() внутри функции say() . Функция display() — вложенная.
При этом в функции display() используется переменная greeting , которая инициализирована вне этой функции, то есть из нелокальной области видимости.
В Python такие переменные как greeting называются свободными переменными.
На самом деле функция display() состоит из двух частей:
Так вот все вместе это называется замыканием (closure).
Замыкание (closure) — это вложенная функция, которая ссылается на одну или более переменных из объемлющей (enclosing) области видимости.
Можно вернуть функцию из функции
В Python функция может возвращать другую функцию. Например, так:
def say(): greeting = "Привет" def display(): print(greeting) return display
В этом примере функция say() возвращает функцию display() , а не выполняет ее.
Когда say() возвращает display() , на самом деле возвращается замыкание:
Следующая инструкция присваивает возвращаемое значение функции say() переменной fn . Поскольку fn — это функция, ее можно вызвать:
Функция say() выполняется и возвращает функцию fn() . Когда выполняется fn() , функция say() уже завершила выполнение.
Это значит, что область видимости функции say() уже не существует к тому моменту, когда выполняется fn() .
Поскольку переменная greeting принадлежит области видимости функции say() , она, по идеи, тоже должна уничтожаться после выполнения say() .
Однако fn() все равно как-то выводит значение переменной greeting , как вы видите в примере выше.
Разберемся, как это работает и почему так происходит.
Ячейки и переменные с несколькими областями видимости
Переменная greeting «разделяется» между двумя областями видимости:
То есть greeting находится одновременно в двух областях видимости. Тем не менее, она всегда ссылается на один и тот же строковый объект «Привет» .
Для этого Python создает промежуточный объект — ячейку (cell).
Узнать адрес ячейки в памяти можно через свойство __closure__ , как показано ниже:
__closure__ возвращает кортеж ячеек.
В этом примере адрес ячейки в памяти — 0x0000017184915C40 . Она ссылается на строковый объект по адресу 0x0000017186A829B0 .
Если вы отобразите адрес памяти строкового объекта в функции say() и замыкании, то увидите, что они ссылаются на один и тот же объект в памяти:
def say(): greeting = "Привет" print(hex(id(greeting))) def display(): print(hex(id(greeting))) print(greeting) return display fn = say() fn()
0x17186a829b0
0x17186a829b0
Когда вы обращаетесь к значению переменной greeting , Python технически дважды «прыгает» в память, чтобы получить значение строки.
Это объясняет, почему когда область видимости функции say() уже не существовала, вы все равно могли обратиться к строковому объекту, на который ссылается переменная greeting .
На основе этого механизма можно определить замыкание по-другому:
Замыкание — это функция и расширенная область видимости, в которой содержатся свободные переменные.
Чтобы узнать, какие свободные переменные содержатся в замыкание, можно использовать __code__.co_freevars . Например:
def say(): greeting = "Привет" def display(): print(greeting) return display fn = say() print(fn.__code__.co_freevars) // Вывод: ('greeting',)
В этом примере fn.__code__.co_freevars вернет переменную greeting — единственную свободную переменную в замыкании fn .
Когда Python сам создает замыкания
Python создает новую область видимости каждый раз, когда выполняется функция. Если функция создает замыкание, Python тоже создаст новое замыкание. Рассмотрите следующий пример:
1. Сначала определим функцию multiplier() , которая возвращает замыкание:
def multiplier(x): def multiply(y): return x * y return multiply
Функция multiplier() возвращает произведение двух аргументов. Но возвращается не само произведение, а замыкание.
2. Теперь вызовем функцию multiplier() три раза:
m1 = multiplier(1) m2 = multiplier(2) m3 = multiplier(3)
Вызовы функция создадут три замыкания. Каждая функция умножает число на 1, 2, и 3 соответсвенно.
3. А теперь выполним функции замыканий:
print(m1(10)) print(m2(10)) print(m3(10))
Как вы видите, у m1 , m2 и m3 разные инстансы замыкания.
Замыкания и цикл for
Предположим, что нам нужно создать все три вышеуказанные замыкания одновременно. Это можно сделать, например, так:
multipliers = [] for x in range(1, 4): multipliers.append(lambda y: x * y) m1, m2, m3 = multipliers print(m1(10)) print(m2(10)) print(m3(10))
Что мы здесь делаем
- Объявляем список, в котором будем хранить замыкания.
- Используем лямбда-выражение, чтобы создать замыкание, и добавляем его в список на каждой итерации.
- «Распоковываем» замыкания из списка и присваиваем их переменным 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 для вычислений нужно использовать в цикле.
def multiplier(x): def multiply(y): return x * y return multiply multipliers = [] for x in range(1, 4): multipliers.append(multiplier(x)) m1, m2, m3 = multipliers print(m1(10)) print(m2(10)) print(m3(10))
СodeСhick.io — простой и эффективный способ изучения программирования.
2023 © ООО «Алгоритмы и практика»
Как вызвать функцию в Python?
Как известно, функции — это блоки инструкций, предназначенные для выполнения определенных задач в программировании. Функции позволяют разбивать большие фрагменты кода на более мелкие отрезки или модули. Позже их можно вызывать отовсюду. Это позволяет повторно использовать определенные части программы и избегать повторений. Функции можно определять внутри классов, модулей, вложенных функций и так далее.
Основные особенности функций в Python:
- Используются чтобы избегать повторений в коде,
- Используются для разделения кода на мелкие модули
- Позволяют скрывать код и создавать ясность для понимания модулей,
- Позволяют повторно использовать код и сохранять память,
- Код из функции можно выполнить только по ее имени,
- Простой синтаксис: def имя_функции(параметры): .
- Для объявления функции в Python используется ключевое слово def .
- Название функции должно начинаться с символа латинского алфавита в любом регистре или нижнего подчеркивания.
- В каждой функции есть двоеточие и отступ, после которого записывается сам код программы.
- Зарезервированные ключевые слова не могут использоваться в качестве названия функции.
- Функция может содержать несколько параметров или не иметь их совсем.
Создание функции в Python
Для создания нужно написать ключевое слово def . Синтаксис следующий:
def function_name(): # логика функции return result # возврат значения
Создадим и вызовем реальную функцию в Python:
Как создать и использовать вложенные функции в Python
Изучите создание и использование вложенных функций в Python для структурирования кода, инкапсуляции и реализации декораторов.
Вложенные функции, или внутренние функции, являются темой, которую нужно освоить при изучении Python. В этой статье мы разберемся, что такое вложенные функции, как их создавать и использовать, а также приведем примеры кода.
Что такое вложенные функции?
Вложенные функции — это функции, определенные внутри других функций. Они могут быть использованы для повышения структурированности кода, инкапсуляции и реализации декораторов.
Создание вложенных функций
Чтобы создать вложенную функцию, достаточно определить новую функцию внутри существующей функции. Вот простой пример:
def outer_function(): print("Я внешняя функция") def inner_function(): print("Я внутренняя функция") inner_function() outer_function()
В этом примере мы создали внутреннюю функцию inner_function внутри внешней функции outer_function . Затем мы вызвали внутреннюю функцию из внешней функции. В результате вызова outer_function() будет выведено:
Я внешняя функция
Я внутренняя функция
Использование вложенных функций
Вложенные функции могут быть использованы в различных случаях. Например, они могут служить для инкапсуляции кода, который не должен быть доступен извне.
def outer_function(x): def inner_function(y): return x * y return inner_function(x) result = outer_function(5) print(result) # Вывод: 25
В этом примере мы использовали вложенную функцию, чтобы умножить x на y . Внешняя функция возвращает результат вызова внутренней функции. Внутренняя функция недоступна извне, что позволяет инкапсулировать логику.
📘 Важно отметить, что вложенные функции имеют доступ к переменным внешней функции. Это называется замыканием.
Пример использования вложенных функций для реализации декораторов
Декораторы — это способ изменения поведения функций или методов с помощью вложенных функций. Вот пример создания простого декоратора с использованием вложенных функций:
def my_decorator(func): def wrapper(): print("Что-то происходит перед вызовом функции") func() print("Что-то происходит после вызова функции") return wrapper def my_function(): print("Я функция, которую нужно изменить") decorated_function = my_decorator(my_function) decorated_function()
В этом примере мы создали декоратор my_decorator , который принимает функцию func в качестве аргумента. Внутри декоратора мы определили вложенную функцию wrapper , которая вызывает переданную функцию func и добавляет дополнительное поведение до и после вызова. Затем мы применили декоратор к функции my_function и вызвали полученную функцию.
В результате выполнения кода будет выведено:
Что-то происходит перед вызовом функции
Я функция, которую нужно изменить
Что-то происходит после вызова функции
Теперь вы знаете, как создавать и использовать вложенные функции в Python. Эти знания помогут вам структурировать код, инкапсулировать логику и реализовывать декораторы. Продолжайте изучать Python, и вскоре вы станете опытным разработчиком! 🚀