Зачем нужны функции высшего порядка
В Python функция высшего порядка — это функция, которая удовлетворяет одному или нескольким из следующих критериев:
- позволяет передавать другие функции в качестве параметров;
- может отправлять обратно другие функции;
- создает новые функции в своем теле.
Такие функции считаются мощным инструментом абстрагирования и повторного использования кода. Они позволяют писать более лаконичный и выразительный код, избегать повторов и ошибок.
Встроенные функции высшего порядка
Map. Нужна для преобразования массива путем применения функции к каждому его элементу. В качестве аргумента map() принимает функцию, которая используется для изменения каждого элемента. Конечный массив содержит преобразованные элементы.
Пример:
def mul(i):
return i * i
num = (3, 5, 7, 11, 13)
resu = map(mul, num)
print(resu)
# making the map object readable
mul_output = list(resu)
print(mul_output)
[9, 25, 49, 121, 169]
Min. Выполняет поиск минимального значения в списке. Принимает один обязательный параметр и три необязательных. При желании можно добавить дополнительные объекты в качестве постоянных аргументов, выполнить сортировку с помощью ключа.
Пример:
arr = [1, 2, 3, 4, 5]
min(arr)
=> 1
min(0, -2, 1, 8, 5)
=> -2
students = [
{'name': 'Jimmy', 'age': 15},
{'name': 'Hector', 'age': 18},
{'name': 'Paige', 'age': 16}
]
min(students, key=lambda x: x['age'])
=> {'name': 'Jimmy', 'age': 15}
min([], default=0)
=> 0
Max. Находит максимальное значение в повторяющейся переменной.
Пример:
arr = [1, 2, 3, 4, 5]
max(arr)
=> 5
max(0, -2, 1, 8, 5)
=> 8
students = [
{'name': 'Jimmy', 'age': 15},
{'name': 'Hector', 'age': 18},
{'name': 'Paige', 'age': 16}
]
max(students, key=lambda x: x['age'])
=> {'name': 'Hector', 'age': 18}
max([], default=100)
=> 100
Sort. Метод возвращает новую отсортированную версию списка. Принимает один обязательный и два необязательных аргумента (reverse и key). Обязательный аргумент — список. Необязательный параметр reverse переворачивает отсортированный список, а необязательный параметр key — функция, на основе которой выполняется сортировка.
Пример:
# List of Integers
numbers = [1, 3, 4, 2]
# Sorting list of Integers
numbers.sort()
print(numbers)
# List of Floating point numbers
decimalnumber = [2.01, 2.00, 3.67, 3.28, 1.68]
# Sorting list of Floating point numbers
decimalnumber.sort()
print(decimalnumber)
# List of strings
words = ["Geeks", "For", "Geeks"]
# Sorting list of strings
words.sort()
print(words)
Output:
[1, 2, 3, 4]
[1.68, 2.0, 2.01, 3.28, 3.67]
['For', 'Geeks', 'Geeks']
Filter.Применяется для фильтрации элементов из массива на основе условия. filter() принимает в качестве аргумента функцию, которая используется для проверки каждого элемента. Итоговый массив содержит только те элементы, которые прошли проверку.
Пример:
nums = [5, 10, 23, 64, 42, 53, 93, 2, 0, -14, 6, -22, -13]
#Filter all the odd numbers from the list
odd = filter(lambda p : p%2 != 0, nums)
#Filter all the even numbers from the list
even = filter(lambda p: p%2 == 0, nums)
print("The list of odd numbers is: ", list(odd))
print("The list of even numbers is: ", list(even))
Здесь используются lambda-функции. Они не считаются функциями высшего порядка, но могут выступать в качестве аргументов.
Пример — создание списка словарей, в которых хранятся сведения о книгах. Там будут имена авторов, название, цена. Цель — отфильтровать информацию о тех книгах, стоимость которых превышает фиксированную цену.
books = [
{"Title":"Angels and Demons", "Author":"Dan Brown", "Price":500},
{"Title":"Gone Girl", "Author":"Gillian Flynn", "Price":730},
{"Title":"The Silent Patient", "Author":"Alex Michaelidis", "Price":945},
{"Title":"Before I Go To Sleep", "Author":"S.J Watson", "Price":400}
]
def func(book):
if book["Price"] > 500:
return True
else:
return False
filtered_object = filter(func, books)
for d in filtered_object:
print(dict(d)["Title"])
Output:
Gone Girl
The Silent Patient
Модуль Functools
reduce(). Функция и итерируемая переменная передаются в reduce. Она применяет предоставленную функцию ко всем элементам итерируемой переменной кумулятивно слева направо, прежде чем вернуть одно значение.
Сначала аргумент применяется к первым двум элементам. Значение, возвращаемое этим первоначальным вызовом, становится первым аргументом, третий элемент — вторым аргументом.
reduce() можно использовать для нахождения суммы списка чисел:
from functools import reduce
# Function that returns the sum of two numbers
def add(a, b):
return a + b
# Our Iterable
num_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# add function is passed as the first argument, and num_list is passed as the second argument
sum = reduce(add, num_list)
print(f"Sum of the integers of num_list : {sum}")
# Passing 10 as an initial value
sum = reduce(add, num_list, 10)
print(f"Sum of the integers of num_list with initial value 10 : {sum}")
Output:
Sum of the integers of num_list : 55
Sum of the integers of num_list with initial value 10 : 65
Здесь reduce() нужна для применения add, которая возвращает сумму двух значений к каждой паре элементов в списке чисел. В результате получается сумма всех элементов.
partial(). Если функция принимает два параметра «a» и «b», из нее может быть построена частичная функция с «a» в качестве предварительно заполненного аргумента, и затем она может быть вызвана с «b» в качестве единственного параметра. Метод partial() в Functools используется для создания частичных объектов. Благодаря этому можно сделать репликацию существующих функций с некоторыми уже установленными параметрами.
Пример:
from functools import partial
# Function to scale a value
def scale_data(value, factor):
return value * factor
# Create partial functions for specific scaling factors
scale_by_2 = partial(scale_data, factor=2)
scale_by_10 = partial(scale_data, factor=10)
data = [1, 2, 3, 4, 5]
scaled_by_2 = list(map(scale_by_2, data)) # Scales by 2
scaled_by_10 = list(map(scale_by_10, data)) # Scales by 10
print(scaled_by_2)
print(scaled_by_10)
Output:
[2, 4, 6, 8, 10]
[10, 20, 30, 40, 50]
singledispatch(). Преобразует функцию в универсальный формат. Готовый вариант может вести себя по-разному в зависимости от типа первого аргумента. Чтобы добавить перегруженные реализации, используется атрибут register().
Пример создания метода, который может обрабатывать несколько типов:
from functools import singledispatch
from decimal import Decimal
@singledispatch
def fun(s):
print(s)
@fun.register(float)
@fun.register(Decimal)
def _3(s):
print(round(s, 2))
fun(2.34)
fun(Decimal(4.897))
Output :
2.34
4.90
cached_property(). Преобразует метод класса в свойство, значение которого вычисляется только один раз, а затем кэшируется как обычный атрибут. Кэшированный результат будет доступен до тех пор, пока объект или экземпляр класса не удален.
Пример:
from functools import cached_property, reduce
class ListProduct:
lst = []
def __init__(self):
self.lst = [1, 2, 3, 4, 6, 9]
def product(self):
print("product() method called.")
return reduce(lambda x, y: x * y, self.lst)
@cached_property
def product_of_elements(self):
print("product_of_elements() method called.")
return reduce(lambda x, y: x * y, self.lst)
ListProductObject = ListProduct()
product = ListProductObject.product()
print("Product without caching - ", product)
print("--"*5)
product = ListProductObject.product()
print("Product without caching - ", product)
print("--"*5)
product = ListProductObject.product_of_elements
print("Product with caching - ", product)
print("--"*5)
product = ListProductObject.product_of_elements
print("Product with caching - ", product)
total_ordering(). Методы упорядочения сравнения класса используются для сравнения объектов класса. Для любого класса есть шесть методов упорядочения сравнения:
- __ lt__(): меньше, чем метод;
- __le__(): меньше или равно методу;
- __ gt__(): больше, чем метод;
- __ge__(): больше или равно методу;
- __eq__(): равно методу.
Например, нужно сравнить объекты класса. Для этого предоставляют реализации всех методов упорядочения сравнения в классе.
Можно упростить процесс с помощью total_ordering. Необходимо определить любой из методов упорядочения сравнения, а функция предоставит остальные методы. Класс также должен добавить метод __eq__().
Например:
from functools import total_ordering
@total_ordering
class Person:
def __init__(self, age):
self.age = age
def __lt__(self, other):
return self.age < other.age
def __eq__(self, other):
return self.age == other.age
person1 = Person(10)
person2 = Person(20)
print("Person(10) > Person(20) ? ", person1 > person2)
print("Person(10) == Person(20) ? ", person1 == person2)
print("Person(10) >= Person(20) ? ", person1 >= person2)
print("Person(10) < Person(20) ? ", person1 < person2)
print("Person(10) <= Person(20) ? ", person1 <= person2)
wraps(). Это декоратор, который применяется к первичной функции. Копирует атрибуты __name__ и __doc__.
from functools import wraps
def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
"""A wrapper function"""
# Do something before
result = f(*args, **kwargs)
# Do something after
return result
return wrapper
@my_decorator
def greet():
"""Returns a friendly greeting"""
return 'Hello!'
print(greet.__name__) # Outputs: greet
print(greet.__doc__) # Outputs: Returns a friendly greeting
В чем польза функций высшего порядка
У функций высшего порядка есть ряд преимуществ перед традиционными функциями в программировании. Они позволяют писать более абстрактный, гибкий и многократно используемый код, упрощают тестирование и отладку кода, помогают разбивать сложные задачи на более мелкие и понятные.
Еще одно преимущество — положительное влияние на развитие функционального программирования. Это парадигма, которая делает упор на неизменяемость, чистые функции и возможность компоновки. Использование этих методов помогает писать более модульный код, который прост в использовании и не подвержен ошибкам.
Благодаря им получается делать код более коротким, устранять повторяющиеся или избыточные элементы. Так код будет более понятным и удобным в обслуживании, что сказывается на командной работе — например, когда несколько разработчиков участвуют в одном проекте.
Заключение
Функции высшего порядка предлагают лаконичный способ манипулирования данными, помогают выполнять сложные операций более простым способом. Включение этих функций в код на Python может привести к созданию эффективных и понятных решений. Их можно применять для оптимизации кода, повышения удобочитаемости. Освоение таких методов поможет погрузиться в разработку на Python.