Наталья Кайда 30 января 2023

🐍 Самоучитель по Python для начинающих. Часть 14: Функции высшего порядка, замыкания и декораторы

Разберем важные концепции, связанные с функциями высшего порядка, напишем собственные версии map(), reduce() и filter(), потренируемся в создании декораторов и решим 10 практических заданий.
2
🐍 Самоучитель по Python для начинающих. Часть 14: Функции высшего порядка, замыкания и декораторы

Функции высшего порядка

В программировании (и в математике) функциями высшего порядка называются функции, которые выполняют одно (или оба) из этих действий:

  1. Принимают одну (и более) функций в качестве аргументов.
  2. Возвращают функцию в качестве результата.

Все остальные функции считаются функциями первого порядка. Вот простейший пример обработки нескольких функций первого порядка multiply(), power(), add(), subtract() функцией высшего порядка higher_order():

        def higher_order(function):  # функция высшего порядка
    return function(15)

def multiply(x): # функция первого порядка
    return x * x

def power(x): # функция первого порядка
    return x ** x

def add(x): # функция первого порядка
    return x + x

def subtract(x): # функция первого порядка
    return x - (x * x)

print(higher_order(multiply))
print(higher_order(power))
print(higher_order(add))
print(higher_order(subtract))

    

Вывод:

        225
437893890380859375
30
-210

    

Декораторы в Python

Синтаксис Python позволяет использовать декораторы для получения результата «прохождения» функции первого порядка через функцию высшего порядка. Декоратор – это функция высшего порядка, которая принимает функцию первого порядка и добавляет в результат что-нибудь от себя, не вмешиваясь в логику полученной функции:

        def print_result(f):
    def result(x):
        r = f(x)
        print(f'Результат вычисления: {r}')
        return r
    return result

@print_result
def triple(x):
    return x * 3

@print_result
def divide(x):
    return x / 5

triple(5)
divide(5)

    

Вывод:

        Результат вычисления: 15
Результат вычисления: 1.0

    

Более того, Python позволяет писать функции, которые создают декораторы:

        def print_with(message):
    def result(f):
        def add_message(x):
            r = f(x)
            print(f'{message} {r}')
            return r
        return add_message
    return result

@print_with('Функция вернула результат:')
def power(x):
    return x ** x

power(int(input()))

    

Вывод для n = 5:

        Функция вернула результат: 3125
    

Поскольку функции в Python являются объектами (класса function), при желании их можно добавлять в словари или списки:

        def print_with(message):
    def result(f):
        def add_message(x):
            r = f(x)
            print(f'{message} {r}')
            return r
        return add_message
    return result

functions = []

def function(f):
    functions.append(f)

@function
@print_with('Функция вернула результат:')
def add(x):
    return x + x

print(functions[0](6))

    

Вывод:

        Функция вернула результат: 12
12

    

Порядок перечисления декораторов имеет значение – в приведенном выше примере в стек попала функция add(), уже измененная функцией высшего порядка print_with(). При изменении порядка декораторов результат будет просто 12.

Декораторы очень часто используются при разработке приложений в Python фреймворках – они позволяют программисту использовать мощную функциональность фреймворка, не задумываясь о том, что именно происходит «под капотом». В приведенном ниже примере декоратор app.route обеспечивает маршрутизацию сайта на основе фреймворка Flask:

        from flask import Flask
  
app = Flask(__name__)
 
@app.route('/')
def index():
    return 'Главная страница сайта'

@app.route('/hello')
def hello():
    return 'Привет, добро пожаловать на сайт!'

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, debug=True)
    

При запуске этого примера по адресу http://localhost:8000/ будет выведена надпись «Главная страница сайта», а по адресу http://localhost:8000/hello – «Привет, добро пожаловать на сайт!»

А в этом примере из функции представления фреймворка Django декоратор @login_required проверяет, вошел ли посетитель на сайт, и в зависимости от результата проверки либо показывает ему страницу, заполненную информацией, созданной этим конкретным пользователем, либо перенаправляет на страницу входа:

        @login_required(login_url='/login')
def home(request):
    all_tasks = request.user.tasks.all()
    return render(request, 'index.html', {'tasks': all_tasks })

    

Как работают встроенные функции высшего порядка

В предыдущих главах мы уже неоднократно использовали три самые популярные встроенные функции высшего порядка – map(), reduce() и filter() для обработки наборов данных. В этом примере встроенная функция map() берет на себя ряд преобразований элементов строки:

        >>> lst = list(map(float, input().split(';')))
1;2;3;4;5;6;7;8;9
>>> print(lst)
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

    

Если бы функции map() не было, пришлось бы заниматься этими преобразованиями самостоятельно – с помощью спискового включения:

        sp = input().split(';')
result = [float(i) for i in sp]
print(result)

    

Или с помощью цикла:

        sp = input().split(';')
result = []
for i in sp:
    result.append(float(i))
print(result)

    

Встроенная функция map(), как и любая другая функция высшего порядка, может принимать любую функцию первого порядка и последовательно применять ее ко всем элементам в полученном наборе данных. В приведенном ниже примере встроенная map() принимает пользовательскую функцию divide():

        sp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def divide(x):
    return 1 / x ** 0.5
    
result = list(map(divide, sp))
print(result)

    

Вывод:

        [1.0, 0.7071067811865475, 0.5773502691896258, 0.5, 0.4472135954999579, 0.4082482904638631, 0.3779644730092272, 0.35355339059327373, 0.3333333333333333, 0.31622776601683794]
    

Встроенная функция map() отличается гибкостью – точно такой же результат можно получить, если передать в нее анонимную лямбда-функцию:

        sp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = list(map(lambda x: 1 / x ** 0.5, sp))
print(result)

    

Напишем собственную функцию my_map(), которая будет принимать любую другую функцию первого порядка, например, my_function(), которая повторяет полученное число столько раз, чему оно равно:

        def my_function(n):
    lst = [str(n) for i in range(1, n + 1)]
    return(''.join(lst))
        

def my_map(function, lst):
    result = []
    for i in lst:
        processed_item = function(i)
        result.append(processed_item)
    return result
print(my_map(my_function, [4, 5, 6, 7]))
    

Вывод:

        ['4444', '55555', '666666', '7777777']
    

Как и встроенная map(), пользовательская my_map() может принимать анонимные функции:

        def my_map(function, lst):
    result = []
    for i in lst:
        processed_item = function(i)
        result.append(processed_item)
    return result

print(my_map(lambda x: ''.join([str(x) for i in range(1, x + 1)]), [1, 2, 5, 9]
    

Вывод:

        ['1', '22', '55555', '999999999']
    
***

Отлично! Вы освоили практическое применение функций высшего порядка.

Вы знаете, что такое декораторы, как их использовать на практике и даже понимаете принцип работы встроенных функций вроде map().

Теперь, когда вы знаете, что работает, пора заглянуть "под капот" и узнать, почему это работает. В полной версии урока вас ждут:

  • Глубокое погружение в замыкания (closures) — ключевую концепцию, которая лежит в основе декораторов и многих других продвинутых техник Python.
  • Создание собственных инструментов: вы напишете с нуля аналоги filter, reduce, zip и enumerate, чтобы досконально понять их работу.
  • Продвинутая практика: вы создадите сложные декораторы для подсчета аргументов и измерения производительности кода.

МЕРОПРИЯТИЯ

Комментарии

 
 

ВАКАНСИИ

Добавить вакансию

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

LIVE >

Подпишись

на push-уведомления