Модуль functools

Модуль functools містить деякі функції вищого порядку. Функція вищого порядку приймає одну або більше функцій як вхідні дані та повертає нову функцію. Найкориснішим інструментом у цьому модулі є функція functools.partial.

Для програм, написаних у функціональному стилі, іноді потрібно конструювати варіанти існуючих функцій, в яких деякі з параметрів заповнені. Розгляньмо Python-функцію f(a, b, c); можливо, ви захочете створити нову функцію g(b, c), яка буде еквівалентна f(1, b, c); ви заповнюєте значення одного з параметрів f(). Це називається "часткове застосування функції".

Конструктор для partial() приймає аргументи (function, arg1, arg2, ..., kwarg1=value1, kwarg2=value2). Отриманий об'єкт є викликаним, тому ви можете просто викликати його, щоб викликати function з заповненими аргументами.

Ось невеликий, але реалістичний приклад:

import functools

def log(message, subsystem):
    """Записати вміст 'message' у вказану підсистему."""
    print('%s: %s' % (subsystem, message))

server_log = functools.partial(log, subsystem='server')
server_log('Не вдалося відкрити сокет')

functools.reduce(func, iter, [initial_value]) кумулятивно виконує операцію на всіх елементах ітерованого об'єкту, і тому не може бути застосованою до нескінченних ітерованих об'єктів. func повинна бути функцією, яка приймає два елементи та повертає одне значення. functools.reduce бере перші два елементи A і B, повернуті ітератором, та обчислює func(A, B). Потім запитує третій елемент, C, обчислює func(func(A, B), C), об'єднує цей результат з четвертим елементом та продовжує, поки ітерований об'єкт не буде вичерпано. Якщо початкове значення вказане, воно використовується як початкова точка, і func(initial_value, A) є першим обчисленням:

import operator, functools

print(functools.reduce(operator.concat, ['A', 'BB', 'C']))  # 'ABBC'

print(functools.reduce(operator.mul, [1, 2, 3], 1))         # 6

print(functools.reduce(operator.mul, [], 1))                # 1

Якщо ітерований об'єкт не повертає жодного значення, виникає виняток TypeError:

import operator, functools
functools.reduce(operator.concat, [])   # TypeError: reduce() of empty sequence with no initial value

Якщо ви використовуєте operator.add з functools.reduce, ви додасте всі елементи ітерованого об'єкта. Цей випадок настільки поширений, що існує спеціальний вбудований метод sum для його обчислення:

import functools, operator
print(functools.reduce(operator.add, [1, 2, 3, 4], 0))  # 10

print(sum([1, 2, 3, 4]))    # 10

print(sum([]))  # 0

Для багатьох випадків використання functools.reduce, однак, може бути зрозуміліше просто написати очевидний цикл for:

import functools, operator
# Замість:
product = functools.reduce(operator.mul, [1, 2, 3], 1)

# Ви можете написати:
product = 1
for i in [1, 2, 3]:
    product *= i

print(product)

Пов'язана функція — це itertools.accumulate(iterable, func=operator.add). Вона виконує те саме обчислення, але замість повернення лише кінцевого результату, accumulate() повертає ітератор, який також видає кожен частковий результат:

itertools.accumulate([1, 2, 3, 4, 5]) =>
  1, 3, 6, 10, 15

itertools.accumulate([1, 2, 3, 4, 5], operator.mul) =>
  1, 2, 6, 24, 120

Модуль operator

Модуль operator згадувався раніше. Він містить набір функцій, що відповідають операторам Python. Ці функції часто корисні у функціональному стилі коду, оскільки вони позбавляють вас від написання тривіальних функцій, які виконують одну операцію.

Деякі з функцій у цьому модулі:

  • Математичні операції: add(), sub(), mul(), floordiv(), abs(), ...
  • Логічні операції: not_(), truth().
  • Побітові операції: and_(), or_(), invert().
  • Порівняння: eq(), ne(), lt(), le(), gt(), і ge().
  • Ідентичність об'єктів: is_(), is_not().

Зверніться до документації модуля operator для повного списку.

Текст на цій сторінці є перекладом "Functional Programming HOWTO", автор: A. M. Kuchling. Інформація про копірайт: History and License.