Введение
В этом разделе представлена идея использования функций для создания других функций.
This tutorial is from open-source community. Access the source code
💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал
В этом разделе представлена идея использования функций для создания других функций.
Рассмотрим следующую функцию.
def add(x, y):
def do_add():
print('Adding', x, y)
return x + y
return do_add
Это функция, которая возвращает другую функцию.
>>> a = add(3,4)
>>> a
<function add.<locals>.do_add at 0x7f27d8a38790>
>>> a()
Adding 3 4
7
Обратите внимание, как внутреняя функция ссылается на переменные, определенные внешней функцией.
def add(x, y):
def do_add():
## `x` и `y` определены выше `add(x, y)`
print('Adding', x, y)
return x + y
return do_add
Далее обратите внимание, что эти переменные каким-то образом остаются живыми после завершения add()
.
>>> a = add(3,4)
>>> a
<function do_add at 0x6a670>
>>> a()
Adding 3 4 ## Откуда берутся эти значения?
7
Когда внутреняя функция возвращается в качестве результата, эта внутреняя функция называется заключением (closure).
def add(x, y):
## `do_add` - это замыкание
def do_add():
print('Adding', x, y)
return x + y
return do_add
Основная особенность: Заключение сохраняет значения всех переменных, необходимых для правильного выполнения функции позднее. Представьте замыкание как функцию плюс дополнительную среду, которая хранит значения переменных, на которые оно зависит.
Замыкания являются важной особенностью Python. Однако их использование часто остается незаметным. Общие приложения:
Рассмотрим функцию такого вида:
def after(seconds, func):
import time
time.sleep(seconds)
func()
Пример использования:
def greeting():
print('Hello Guido')
after(30, greeting)
after
выполняет переданную функцию... позже.
Замыкания несут дополнительную информацию.
def add(x, y):
def do_add():
print(f'Adding {x} + {y} -> {x+y}')
return do_add
def after(seconds, func):
import time
time.sleep(seconds)
func()
after(30, add(2, 3))
## `do_add` имеет ссылки x -> 2 и y -> 3
Замыкания также могут быть использованы в качестве техники для избежания избыточного повторения кода. Вы можете писать функции, которые генерируют код.
Одной из более мощных функций замыканий является их использование для генерации повторяющегося кода. Если вы вспомните упражнение 5.7, вспомните код для определения свойства с проверкой типа.
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
...
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, value):
if not isinstance(value, int):
raise TypeError('Expected int')
self._shares = value
...
Вместо того, чтобы многократно повторять этот код, вы можете автоматически создавать его с использованием замыкания.
Создайте файл typedproperty.py
и поместите в него следующий код:
## typedproperty.py
def typedproperty(name, expected_type):
private_name = '_' + name
@property
def prop(self):
return getattr(self, private_name)
@prop.setter
def prop(self, value):
if not isinstance(value, expected_type):
raise TypeError(f'Expected {expected_type}')
setattr(self, private_name, value)
return prop
Теперь, попробуйте его, определив класс следующим образом:
from typedproperty import typedproperty
class Stock:
name = typedproperty('name', str)
shares = typedproperty('shares', int)
price = typedproperty('price', float)
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
Попробуйте создать экземпляр и проверить, работает ли проверка типа.
>>> s = Stock('IBM', 50, 91.1)
>>> s.name
'IBM'
>>> s.shares = '100'
... должен получиться TypeError...
>>>
В вышеприведенном примере пользователи могут найти вызовы, такие как typedproperty('shares', int)
, несколько громоздкими для ввода - особенно если они повторяются часто. Добавьте следующие определения в файл typedproperty.py
:
String = lambda name: typedproperty(name, str)
Integer = lambda name: typedproperty(name, int)
Float = lambda name: typedproperty(name, float)
Теперь перепишите класс Stock
для использования этих функций вместо этого:
class Stock:
name = String('name')
shares = Integer('shares')
price = Float('price')
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
Ах, это немного лучше. Главное, что можно извлечь здесь, - это то, что замыкания и lambda
часто могут быть использованы для упрощения кода и устранения раздражающего повторения. Это часто хорошо.
Поздравляем! Вы завершили лабораторную работу по возврату функций. Вы можете практиковаться в более лабораторных работах в LabEx, чтобы улучшить свои навыки.