Python Функціональне програмування - один із 4-ох основних стилів програмування
Python Функціональне програмування обов'язково потрібно постаратися освоїти, щоб використовувати переваги, які воно надає.
Функціональне програмування на Python
Функціональне програмування є однією з парадигм, що підтримуються мовою програмування Python.
Основними передумовами для повноцінного функціонального програмування в Python є функції вищих порядків, розвинені засоби обробки списків, рекурсія, можливість організації лінивих обчислень.
Елементи функціонального програмування в Python можуть бути корисними для будь-якого програміста, оскільки дозволяють гармонійно поєднувати виразну потужність цього підходу з іншими підходами.
Зміст
- 1 Можливості
- 1.1 Визначення та використання функції
- 1.2 Спискові включення
- 1.3 Вбудовані функції вищих систем
- 1.3.1 map()
- 1.3.2 filter()
- 1.3.3 reduce()
- 1.3.4 apply()
- 1.4 Замикання
- 1.5 Ітератори
- 1.6 Модуль functools
- 1.7 Ліниві обчислення
- 1.8 Функтори
- 2 Примітки
- 3 Посилання
- 4 Література
Можливості
Визначення та використання функції
Функція Python може бути визначена за допомогою оператораdefабо лямбда-виразом. Наступні оператори еквівалентні:
def func(x , y ): return x ** 2 + y ** 2
func = lambda x , y : x ** 2 + y ** 2
У визначенні функції фігурують формальні аргументи. Деякі з них можуть мати значення за промовчанням. Усі аргументи зі значеннями за промовчанням слідують після аргументів без значень за промовчанням.
При виклику функціїзадаються фактичні аргументи. Наприклад:
Спочатку йдуть позиційні аргументи. Вони порівнюються з іменами формальних аргументів по порядку. Потім йдуть іменовані аргументи. Вони зіставляються за іменами і може бути задані у виклику функції у порядку.
Вочевидь, всі аргументи, котрим у описі функції не зазначені значення за промовчанням, мають бути у виклику функції. Повтори в іменах аргументів неприпустимі.
Функція завжди повертає лише одне значення (абоNone, якщо значення не задано в операторіreturnабо цей оператор не зустрінутий після досягнення кінця визначення функції). Однак, це незначне обмеження, оскільки значенням, що повертається, може бути кортеж.
Визначивши функцію з допомогою лямбда-выражения, можна відразу використовувати:
>>> ( lambda x : x + 2 )( 5 ) 7
Лямбда-вирази зручні для визначення не дуже складних функцій, які передаються потім іншим функціям.
Функції в Python є об'єктами першого класу, тобто, можуть використовуватися у програмі поруч із об'єктами інших типів даних.
Спискові включення
Спискове включення (англ. list comprehension ) - найбільш виразне з функціональних засобів Python. Наприклад, для обчислення списку квадратів позитивних цілих чисел менших 10 можна використовувати вираз:
Вбудовані функції вищих порядків
У Python є функції, одним із аргументів яких є інші функції:map(),filter(),reduce(),apply().
Функціяmap()дозволяє обробляти одну або кілька послідовностей за допомогою заданої функції:
Аналогічного (тільки при однаковій довжині списків) результату можна досягти за допомогоюспискових виразів:
Функціяfilter()дозволяє фільтрувати значення послідовності. У результуючому списку лише ті значення, для яких значення функції для елемента є істинним:
Те саме за допомогою спискових виразів:
Для організації ланцюжкових обчислень у списку можна використовувати функціюreduce(). Наприклад, добуток елементів списку може бути обчислений так (Python 2):
Обчислення відбуваються у такому порядку:
Ланцюжок викликів зв'язується за допомогою проміжного результату (res). Якщо список порожній, просто використовується третій параметр (у разі нуля множників це 1):
Зрозуміло, проміжний результат є необов'язковим числом. Це може бути будь-який інший тип даних, зокрема список. Наступний приклад показуєреверссписку:
Для найбільш поширених операцій у Python є вбудовані функції:
У Python 3 вбудованої функціїreduce()немає, але її можна знайти в модуліfunctools.
Функція для застосування іншої функції до позиційних та іменованих аргументів, заданих списком та словником відповідно (Python 2):
У Python 3 замість функціїapply()слід використовувати спеціальний синтаксис:
Функції, що визначаються всередині інших функцій, є повноцінними замикання (англ.closures):
Інші засоби функціонального програмування доступні зі стандартної бібліотеки (наприклад, модульitertools) та інших бібліотек.
Наступний приклад ілюструє застосування перелічуючого і сортуючого ітераторів (ітератор не може бути надрукований операторомprint, тому значення, що залишилися в ньому, були поміщені в список):
Наступний приклад ілюструє використання модуляitertools:
У наступному прикладі ілюструється функціяgroupby(групувати за), за допомогою якої породжується список пар значення ключа і відповідний ключ ітератор (в цей ітератор зібрані всі значення вихідного списку з однаковим значенням ключа). У прикладі ключем є True чи False залежно від позитивності значення. (Для цілей виведення кожен ітератор перетворюється на список).
У модуліitertoolsє й інші функції для роботи з ітераторами, що дозволяють коротко (у функціональному стилі) і з обчислювальної точки зору ефективно висловити необхідні процеси обробки списків.
Модуль functools
У Python 2.5 з'явився модульfunctoolsі зокрема можливістьчасткового застосування функцій:
(Часткове застосування функцій також можна реалізувати за допомогою замикань чи функторів)
Лениві обчислення
Ліниві обчислення можна організувати в Python кількома способами, використовуючи різні механізми:
- найпростіші логічні операції or та and не обчислюють другий операнд, якщо результат визначається першим операндом
- лямбда-вирази
- визначені користувачем класи з лінивою логікою обчислень або функтори
- Генератори та генераторні вирази
- (Python 2.5) if-вираз має «ліниву» семантику (обчислюється тільки той операнд, який потрібен)
Приклад, що ілюструє роботу if-виразу. За допомогою оператораprintможна простежити, які функції реально викликали:
Деякі приклади з книги рецептів:
- Лінива сортування
- Лінивий обхід графа
- Ліниве обчислення властивостей
- Каррінг
Функтори
Функтораминазивають об'єкти, синтаксично подібні до функцій, тобто підтримують операцію виклику. Для визначення функтора потрібно перевантажити оператор()за допомогою методу__call__.
У Python функтори повністю аналогічні функціям, крім спеціальних атрибутів (func_codeта інших). Наприклад, функтори можна передавати як функції зворотного виклику (callback) в С-код.
Функтори дозволяють замінити деякі прийоми, пов'язані з використанням замикання, статичних змінних тощо.
Нижче представлене замикання та еквівалентний йому функтор:
Слід зазначити, що код, що використовує замикання, виконуватиметься швидше, ніж код із функтором. Це пов'язано з необхідністю отримання атрибутуvalу змінноїself(тобто функтор робить одну Python операцію більше).
Функтори також не можна використовувати для створення декораторів з параметрами.
З іншого боку, функторам доступні всі можливості ОВП у Python, що робить їх дуже корисними для функціонального програмування.
Наприклад, можна написати функтор, який «запам'ятовуватиме» виконувані над ним операції і потім повторювати їх. І тому досить відповідним чином перевантажити спеціальні методи.
Функтори привносять у Python можливість лінивих обчислень, властиву функціональним мовам: замість обчислення результату висловлювання — динамічне визначення нових функцій комбінуванням наявних.
Певний функцій створює значні накладні витрати, так як при кожному виклику проходить по викликах всіх вкладенихlambda.
Можна оптимізувати функтор, застосувавши техніку генерування байт-коду під час виконання. Відповідний приклад та тести на швидкість є в ПрикладахPython програм.
При використанні цієї техніки швидкість виконання не відрізнятиметься від «статичного» коду (якщо не брати до уваги часу, що потрібно на одноразове конструювання результуючої функції).
Замість байт-коду Python можна генерувати на виході, наприклад, код мовою програмування C, іншими мовами програмування або файлами XML.
Незважаючи на накладні витрати, ліниве обчислення може дати помітний виграш у швидкості у випадках, коли дії, що обертаються лінивим функтором, досить дорогі, наприклад, включають об'ємні обчислення або доступ до диска.
Припустимо деякий проміжний результатXліниво обчислюється перед умовним оператором; для нього буде створено ланцюжок функторів.
У тій галузі умовного оператора, де значенняXне потрібно по ходу обчислення, цей ланцюжок функторів буде просто відкинуто, не привівши до дорогого обчислення.
В іншій гілці, деXпотрібно для обчислення кінцевого результату функції, ланцюжок функторов здійснить його обчислення.
При цьому програмісту не потрібно відстежувати, в якій із гілок алгоритму значення може не знадобитися: він може розраховувати, що обчисленняXвідбудеться лише тоді, коли його результат не буде відкинутий.