Создание функциональных функций

PythonPythonBeginner
Практиковаться сейчас

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

Заключения (Closures)

Когда внутреняя функция возвращается в качестве результата, эта внутреняя функция называется заключением (closure).

def add(x, y):
    ## `do_add` - это замыкание
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

Основная особенность: Заключение сохраняет значения всех переменных, необходимых для правильного выполнения функции позднее. Представьте замыкание как функцию плюс дополнительную среду, которая хранит значения переменных, на которые оно зависит.

Использование замыканий (Closures)

Замыкания являются важной особенностью Python. Однако их использование часто остается незаметным. Общие приложения:

  • Использование в обратных вызовах (callback функциях).
  • Отложенная оценка (delayed evaluation).
  • Функции-декораторы (далее).

Отложенная оценка (Delayed Evaluation)

Рассмотрим функцию такого вида:

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

Повторение кода

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

Упражнение 7.7: Использование замыканий для избежания повторений

Одной из более мощных функций замыканий является их использование для генерации повторяющегося кода. Если вы вспомните упражнение 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...
>>>

Упражнение 7.8: упрощение вызовов функций

В вышеприведенном примере пользователи могут найти вызовы, такие как 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, чтобы улучшить свои навыки.

OSZAR »