Функциональное программирование на Питоне
Материал из Википедии — свободной энциклопедии
Функциональное программирование является одной из парадигм, поддерживаемых языком программирования Python. Основными предпосылками для полноценного функционального программирования в Python являются: функции высших порядков, развитые средства обработки списков, рекурсия, возможность организации ленивых вычислений. Элементы функционального программирования в Питоне могут быть полезны любому программисту, так как позволяют гармонично сочетать выразительную мощность этого подхода с другими подходами.
Содержание |
[править] Возможности
[править] Списочные выражения
Списочные выражения (list comprehension, иногда используется термин "списковые включения") — наиболее выразительное из функциональных средств Питона. Например, для вычисления списка квадратов натуральных чисел, меньших 10, можно использовать выражение:
l = [x**2 for x in range(10)]
[править] Определение и использование функции
Функция в Питоне может быть определена с помощью оператора def
или лямбда-выражением. Следующие операторы эквивалентны:
def func(x, y): return x**2 + y**2
func = lambda x, y: x**2 + y**2
В определении функции фигурируют формальные аргументы. Некоторые из них могут иметь значения по умолчанию. Все аргументы со значениями по умолчанию следуют после аргументов без значений по умолчанию.
При вызове функции задаются фактические аргументы. Например:
func(2, y=7)
В начале идут позиционные аргументы. Они сопоставляются с именами формальных аргументов по порядку. Затем следуют именованные аргументы. Они сопоставляются по именам и могут быть заданы в вызове функции в любом порядке. Разумеется, все аргументы без значений по умолчанию должны быть заданы. Повторы в именах аргументов недопустимы.
Функция всегда возвращает только одно значение (или None
, если значение не задано в операторе return
или этот оператор не встречен по достижении конца определения функции). Однако, это незначительное ограничение, так как возвращаемым значением может быть кортеж.
Определив функцию с помощью лямбда-выражения, можно тут же ее использовать:
>>> (lambda x: x+2)(5) 7
Лямбда-выражения удобны для определения не очень сложных функций, которые передаются затем другим функциям.
[править] Встроенные функции высших порядков
В Питоне есть функции, одним из аргументом которых являются другие функции: map()
, filter()
, reduce()
, apply()
.
[править] map()
Функция map()
позволяет обрабатывать одну или несколько последовательностей с помощью заданной функции:
>>> spisok1 = [7, 2, 3, 10, 12] >>> spisok2 = [-1, 1, -5, 4, 6] >>> map(lambda x, y: x*y, spisok1, spisok2) [-7, 2, -15, 40, 72]
Аналогичного (только при одинаковой длине списков) результата можно добиться с помощью списковых выражений:
>>> [x*y for x, y in zip(spisok1, spisok2)] [-7, 2, -15, 40, 72]
[править] filter()
Функция filter()
позволяет фильтровать значения последовательности. В результирующем списке только те значения, для которых значение функции для элемента истинно:
>>> spisok = [10, 4, 2, -1, 6] >>> filter(lambda x: x < 5, spisok) # В результат попадают только те элементы x, для которых x < 5 истинно [4, 2, -1]
Тоже самое с помощью списковых выражений:
>>> spisok = [10, 4, 2, -1, 6] >>> [x for x in spisok if x < 5] [4, 2, -1]
[править] reduce()
Для организации цепочечных вычислений в списке можно использовать функцию reduce()
. Например, произведение элементов списка может быть произведено так:
>>> spisok = [1, 2, 3, 4, 5] >>> reduce(lambda res, x: res*x, spisok, 1) 120
Вычисления происходят в следующем порядке:
((((1*1)*2)*3)*4)*5
Цепочка вызовов связывается с помощью промежуточного результата (res
). Если список пустой, просто используется третий параметр (в случае произведения нуля множителей это 1):
>>> reduce(lambda res, x: res*x, [], 1) 1
Разумеется, промежуточный результат необязательно число. Это может быть любой другой тип данных, в том числе и список. Следующий пример показывает реверс списка:
>>> reduce(lambda res, x: [x]+res, [1, 2, 3, 4], []) [4, 3, 2, 1]
Для наиболее распространенных операций в Питоне есть встроенные функции:
>>> spisok = [1, 2, 3, 4, 5] >>> sum(spisok) 15 >>> list(reversed(spisok)) [5, 4, 3, 2, 1]
[править] apply()
Функция для применения другой функции к позиционным и именованным аргументам, заданным списком и словарем соответственно:
>>> def f(x, y, z, a=None, b=None): ... print x, y, z, a, b ... >>> apply(f, [1, 2, 3], {'a': 4, 'b': 5}) 1 2 3 4 5
В последних версиях Питона вместо функции apply()
можно применить специальный синтаксис (в продолжение примера выше):
>>> f(*[1, 2, 3], **{'a': 4, 'b': 5}) 1 2 3 4 5
[править] Замыкания
Функции, определяемые внутри других функций, представляют собой полноценные замыкания (англ. closures):
def multiplier(n): "multiplier(n) возвращает функцию, умножающую на n" def mul(k): return n*k return mul # того же эффекта можно добиться выражением # multiplier = lambda n: lambda k: n*k mul2 = multiplier(2) # mul2 - функция, умножающая на 2, например, mul2(5) == 10
[править] Итераторы
Другие средства функционального программирования доступны из стандартной библиотеки (например, модуль itertools
) и других библиотек.
Следующий пример иллюстрирует применение перечисляющего и сортирующего итераторов (итератор не может быть напечатан оператором print
, поэтому оставшиеся в нем значения были помещены в список):
>>> it = enumerate(sorted("PYTHON")) # итератор для перечисленных отсортированных букв слова >>> it.next() # следующее значение (0, 'H') >>> print list(it) # оставшиеся значения в виде списка [(1, 'N'), (2, 'O'), (3, 'P'), (4, 'T'), (5, 'Y')]
Следующий пример иллюстрирует использование модуля itertools
:
>>> from itertools import chain >>> print list(chain(iter("ABC"), iter("DEF"))) ['A', 'B', 'C', 'D', 'E', 'F']
В следующем примере иллюстрируется функция groupby
(группировать по), с помощью которой порождается список пар значение ключа и соответствующий ключу итератор (в этот итератор собраны все значения исходного списка с одинаковым значением ключа). В примере ключом является True или False в зависимости от положительности значения. (Для целей вывода каждый итератор превращается в список).
from math import cos from itertools import groupby lst = [cos(x*.4) for x in range(30)] # косинусоида [list(y) for k, y in groupby(lst, lambda x: x > 0)] # группы положительных и отрицательных чисел
В модуле itertools
есть и другие функции для работы с итераторами, позволяющие кратко (в функциональном стиле) и с вычислительной точки зрения - эффективно - выразить требуемые процессы обработки списков.
[править] Модуль functools
В Python 2.5 появился модуль functools
и в частности возможность частичного применения функций:
>>> from functools import partial >>> def myfun(a, b): return a + b ... >>> myfun1 = partial(myfun, 1) >>> print myfun1(2) 3
[править] Ленивые вычисления
Ленивые вычисления можно организовать в Python несколькими способами, используя различные механизмы:
- простейшие логические операции or и and не вычисляют второй операнд, если результат определяется первым операндом
- лямбда-выражения
- определенные пользователем классы с ленивой логикой вычислений[1]
- Генераторы и генераторные выражения
- (Python 2.5) if-выражение имеет "ленивую" семантику (вычисляется только тот операнд, который нужен)
Пример, который иллюстрирует работу if-выражения. С помощью оператора print
можно проследить, какие функции реально вызывались:
>>> def f(): ... print "f" ... return "f" ... >>> def g(): ... print "g" ... return "g" ... >>> f() if True else g() f 'f' >>> f() if False else g() g 'g'
Некоторые примеры из книги рецептов:
[править] Ссылки
- Functional Programming with Python, Pramode C.E., Linux Gazette, 2004
- Charming Python: Functional programming in Python, David Mertz
- Набор инструментов Xoltar для ФП (и не только)