акжеТакПопулярный язык программирования Python больше известен благодаря объектно-ориентированному программированию. Но Python также хорош для функционального программирования. Здесь вы можете узнать, какие функции доступны и как их использовать.
- Каковы особенности функционального программирования?
- Функциональное программирование декларативно
- Рекурсия используется как альтернатива итерации
- Насколько хорош Python для функционального программирования?
- Функции в Python — граждане первого класса
- Лямбды — это анонимные функции в Python.
- Функция высшего порядка в Python
- Итерации, включения и генераторы
- Какие альтернативы Python существуют для функционального программирования?
Каковы особенности функционального программирования?
Функциональное программирование — это стиль программирования, в котором функции используются в качестве основной единицы кода. Происходит постепенная градация от чисто функциональных языков, таких как Haskell или Lisp, к мультипарадигмальным языкам, таким как Python. Таким образом, различие в том, поддерживает ли язык функциональное программирование, не является черным или белым.
Чтобы функциональное программирование было возможно на языке, эти функции должны рассматриваться как так называемые граждане первого класса. Это дано в Python, где функции являются объектами, точно так же, как строки, числа и списки. Функции могут передаваться другим функциям в качестве параметров или возвращаться функциями в качестве возвращаемых значений.
Функциональное программирование декларативно
При использовании декларативного программирования вы описываете проблему, а поиск решения оставляете на усмотрение локали. Напротив, императивный подход основан на пошаговом описании пути к решению. Функциональное программирование относится к декларативному подходу; Оба подхода можно использовать с Python.
Давайте посмотрим на конкретный пример в Python. Дан список номеров. Мы хотим вычислить соответствующие квадратные числа. Сначала покажем императивный подход :
# Calculate squares from list of numbers def squared(nums): # Start with empty list squares = [] # Process each number individually for num in nums: squares.append(num ** 2) return squares
Используя списки, Python поддерживает декларативный подход, который можно легко комбинировать с функциональными методами. Мы генерируем список квадратных чисел без явного цикла. Полученный код намного компактнее и не требует отступов:
# Numbers 0–9 nums = range(10) # Calculate squares using list expression squares = [num ** 2 for num in nums] # Show that both methods give the same result assert squares == squared(nums)
Чистые функции предпочтительнее процедур
Чистая функция сравнима с основными математическими функциями. Термин обозначает функцию со следующими свойствами:
- Функция возвращает тот же результат для тех же аргументов.
- Функция имеет доступ только к своим аргументам.
- Функция не вызывает никаких побочных эффектов.
В совокупности эти свойства означают, что при вызове чистой функции окружающая система не меняется. Рассмотрим классический пример функции квадрата f(x) = x * x. Это можно легко реализовать на Python в виде чистой функции:
# Numbers 0–9 nums = range(10) # Calculate squares using list expression squares = [num ** 2 for num in nums] # Show that both methods give the same result assert squares == squared(nums)
В отличие от чистых функций процедуры, распространенные в более старых языках, таких как Pascal и Basic. Как и функция, процедура представляет собой именованный блок кода, который можно вызывать несколько раз. Однако процедура не возвращает значение. Вместо этого процедуры напрямую обращаются к нелокальным переменным и изменяют их по мере необходимости.
В C и Java процедуры реализованы как функции с возвращаемым типом void. В Python функция всегда возвращает значение : если оператор return отсутствует, возвращается специальное значение «Нет». Поэтому, когда мы говорим о процедурах в Python, мы имеем в виду функцию без оператора возврата.
Давайте рассмотрим несколько примеров чистых и нечистых функций в Python. Следующая функция является нечистой, поскольку при каждом вызове она возвращает другой результат:
# Function without arguments def get_date(): from datetime import datetime return datetime.now()
Также следующая процедура является нечистой, потому что она обращается к данным, определенным вне функции:
# Function using non-local value name = 'John' def greetings_from_outside(): return(f"Greetings from {name}")
Следующая функция является нечистой, поскольку при вызове она изменяет изменяемый аргумент, тем самым влияя на окружающую систему:
# Function modifying argument def greetings_from(person): print(f"Greetings from {person['name']}") # Changing `person` defined somewhere else person['greeted'] = True return person # Let's test person = {'name': "John"} # Prints `John` greetings_from(person) # Data was changed from inside function assert person['greeted']
Следующая функция является чистой, поскольку возвращает один и тот же результат для одного и того же аргумента и не имеет побочных эффектов:
# Pure function def squared(num): return num * num
Рекурсия используется как альтернатива итерации
В функциональном программировании рекурсия является противоположностью итерации. Рекурсивная функция многократно вызывает себя для получения результата. Чтобы это работало без функции, вызывающей бесконечный цикл, должны быть выполнены два условия:
- Рекурсия должна завершиться при достижении базового случая.
- При рекурсивном выполнении функции проблема должна быть уменьшена.
Python поддерживает рекурсивные функции. В качестве известного примера мы показываем расчет последовательности Фибоначчи. Это так называемый наивный подход ; это неэффективно для больших значений n, но может быть легко оптимизировано с помощью кэширования:
def fib(n): if n == 0 or n == 1: return n else: return fib(n - 2) + fib(n - 1)
Насколько хорош Python для функционального программирования?
Python — мультипарадигмальный язык, т.е. ЧАС. при написании программ можно использовать различные парадигмы программирования. В дополнение к функциональному программированию на Python может быть легко реализовано, в частности, объектно-ориентированное программирование.
В Python есть множество инструментов для функционального программирования. Однако, в отличие от чисто функциональных языков, таких как Haskell, область применения ограничена. Степень функционального программирования программы на Python зависит в первую очередь от программиста. Мы предоставляем обзор ключевых функциональных особенностей Python.
Функции в Python — граждане первого класса
В Python «все является объектом», и это также относится к функциям. Функции можно использовать в любом месте языка, где разрешены другие объекты. Давайте рассмотрим конкретный пример: мы хотим запрограммировать калькулятор, поддерживающий различные математические операции.
Сначала покажем императивный подход. При этом используются классические инструменты структурного программирования, такие как условные переходы и операторы присваивания:
def calculate(a, b, op='+'): if op == '+': result = a + b elif op == '-': result = a - b elif op == '*': result = a * b elif op == '/': result = a / b return result
Теперь рассмотрим декларативный подход к решению той же задачи. Вместо ветки if мы отображаем операции как словарь Python. Символы операций относятся к клавишам соответствующих функциональных объектов, которые мы импортируем из модуля оператора. Полученный код более понятен и обходится без ветвлений:
def calculate(a, b, op='+'): # Import operator functions import operator # Map operation symbols to functions operations = { '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv, } # Choose operation to carry out operation = operations[op] # Run operation and return results return operation(a, b)
Затем мы тестируем нашу декларативную функцию вычисления. Операторы assert показывают, что наш код работает:
# Let's test a, b = 42, 51 assert calculate(a, b, '+') == a + b assert calculate(a, b, '-') == a - b assert calculate(a, b, '*') == a * b assert calculate(a, b, '/') == a / b
Лямбды — это анонимные функции в Python.
В дополнение к хорошо известному способу определения функций в Python с использованием ключевого слова def, язык знает так называемые «лямбды». Это короткие анонимные (читай: безымянные) функции, определяющие выражение с параметрами. Лямбда-выражения можно использовать везде, где ожидается функция, или присвоить имя:
squared = lambda x: x * x assert squared(9) == 81
Используя лямбда-выражения, мы можем улучшить нашу функцию вычисления. Вместо жесткого кодирования доступных операций внутри функции мы передаем dict с лямбда-функциями в качестве значений. Это позволяет нам легко добавлять новые операции позже:
def calculate(a, b, op, ops={}): # Get operation from dict and define noop for non-existing key operation = ops.get(op, lambda a, b: None) return operation(a, b) # Define operations operations = { '+': lambda a, b: a + b, '-': lambda a, b: a - b, } # Let's test a, b, = 42, 51 assert calculate(a, b, '+', operations) == a + b assert calculate(a, b, '-', operations) == a - b # Non-existing key handled gracefully assert calculate(a, b, '**', operations) == None # Add a new operation operations['**'] = lambda a, b: a ** b assert calculate(a, b, '**', operations) == a ** b
Функция высшего порядка в Python
Лямбда-выражения часто используются в сочетании с функциями более высокого порядка, такими как map() и filter(). Таким образом, элементы итерируемого объекта могут быть преобразованы без использования циклов. Функция map() принимает функцию и итерируемый объект в качестве параметров и выполняет функцию для каждого элемента итерируемого объекта. Рассмотрим снова задачу генерации квадратных чисел:
nums = [3, 5, 7] squares = map(lambda x: x ** 2, nums) assert list(squares) == [9, 25, 49]
Элементы итерируемого объекта могут быть отфильтрованы с помощью функции filter(). Мы расширяем наш пример, чтобы генерировались только четные квадратные числа:
nums = [1, 2, 3, 4] squares = list(map(lambda num: num ** 2, nums)) even_squares = filter(lambda square: square % 2 == 0, squares) assert list(even_squares) == [4, 16]
Итерации, включения и генераторы
Итерации — это основная концепция Python. Это абстракция коллекций, элементы которых можно выводить по отдельности. К ним относятся вы. Строки, кортежи, списки и словари — все по одним и тем же правилам. Например, область действия итерируемого объекта можно запросить с помощью функции len():
name = 'Walter White' assert len(name) == 12 people = ['Jim', 'Jack', 'John'] assert len(people) == 3
Понимания используются на основе итерируемых объектов. Они хорошо подходят для функционального программирования и в значительной степени заменили использование лямбда-выражений с помощью map() и filter().
# List comprehension to create first ten squares squares = [num ** 2 for num in range(10)]
Как известно из чисто функциональных языков, Python предлагает подход к отложенным вычислениям с помощью так называемых генераторов. Это означает, что данные генерируются только при доступе к ним. Это экономит много памяти. Мы показываем выражение генератора, которое вычисляет каждое квадратное число при доступе:
# Generator expression to create first ten squares squares = (num ** 2 for num in range(10))
Ленивые функции могут быть реализованы в Python с помощью оператора yield. Напишем функцию, которая возвращает положительные числа до заданного предела:
def N(limit): n = 1 while n <= limit: yield n n += 1
Какие альтернативы Python существуют для функционального программирования?
Функциональное программирование уже давно пользуется большой популярностью и зарекомендовало себя как основное противодействие объектно-ориентированному программированию. Сочетание неизменяемых структур данных с чистыми функциями позволяет легко распараллелить код. Поэтому функциональное программирование особенно интересно для преобразования данных в конвейеры данных.
Чисто функциональные языки с сильными системами типов, такие как Haskell или диалект Lisp Clojure, особенно сильны. С другой стороны, JavaScript также считается функциональным языком. С TypeScript доступна современная альтернатива со строгой типизацией.








