Функции высшего порядка
В программировании (и в математике) функциями высшего порядка называются функции, которые выполняют одно (или оба) из этих действий:
- Принимают одну (и более) функций в качестве аргументов.
- Возвращают функцию в качестве результата.
Все остальные функции считаются функциями первого порядка. Вот простейший пример обработки нескольких функций первого порядка 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
, чтобы досконально понять их работу. - Продвинутая практика: вы создадите сложные декораторы для подсчета аргументов и измерения производительности кода.
Комментарии