В Python частичное применение функции (каррирование) против явного определения функции - PullRequest
16 голосов
/ 23 февраля 2011

В Python считается ли это лучшим стилем для:

  • явного определения полезных функций в терминах более общих, возможно, внутреннего использования, функций;или
  • использовать приложение с частичной функцией для явного описания функции каррирования?

Я объясню свой вопрос в надуманном примере.

Предположим, кто-то пишет функцию, _sort_by_scoring, который принимает два аргумента: функцию оценки и список элементов.Он возвращает копию исходного списка, отсортированного по оценкам на основе позиции каждого элемента в исходном списке.Предоставляются также две примерные функции оценки:

def _sort_by_score(scoring, items_list):
    unsorted_scored_list = [(scoring(len(items_list), item_position), item) for item_position, item in enumerate(items_list)]
    sorted_list = [item for score, item in sorted(unsorted_scored_list)]
    return sorted_list

def _identity_scoring(items_list_size, item_position):
    return item_position

def _reversed_scoring(items_list_size, item_position):
    return items_list_size - item_position

Функция _sort_by_score никогда не вызывается напрямую;вместо этого он вызывается другими функциями с одним аргументом, которые передают функцию оценки и свой единственный аргумент (список элементов) в _sort_by_scoring и возвращают результат.

# Explicit function definition style
def identity_ordering(items_list):
    return _sort_by_score(_identity_scoring, items_list)

def reversed_ordering(items_list):
    return _sort_by_score(_reversed_scoring, items_list)

Очевидно, что это намерение лучше выражается вусловия каррирования функций.

# Curried function definition style
import functools
identity_ordering = functools.partial(_sort_by_score, _identity_scoring)
reversed_ordering = functools.partial(_sort_by_score, _reversed_scoring)

Использование (в любом случае):

>>> foo = [1, 2, 3, 4, 5]
>>> identity_ordering(foo)
[1, 2, 3, 4, 5]
>>> reversed_ordering(foo)
[5, 4, 3, 2, 1]

Очевидные преимущества стиля явного определения функции:

  1. полезные функциимогут быть определены до более общих функций, без вызова NameErrors;
  2. вспомогательные функции (например, функции скоринга) могут быть определены в теле определения функции;
  3. возможно легче отладить;
  4. код выглядит хорошо благодаря тому, что «явное лучше, чем неявное».

Очевидные преимущества стиля определения функции с карри:

  1. выражает намерение функционального программирования идиоматически;
  2. код выглядит красиво благодаря краткости.

Для определения "полезных" функций, которые изон два стиля предпочтительнее?Есть ли другие стили, более идиоматические / Pythonic / и т. Д.?

Ответы [ 2 ]

13 голосов
/ 23 февраля 2011

Если вы хотите, чтобы функции curry были частью общедоступного интерфейса, используйте явные определения функций.Это имеет следующие дополнительные преимущества:

  1. Проще всего назначить строку документации явному определению функции.Для partial() функций вы должны будете присвоить атрибуту __doc__, что несколько уродливо.

  2. Реальные определения функций легче просматривать при просмотре источника модуля.

Я бы использовал functools.partial() аналогично лямбда-выражениям, то есть для локально необходимых одноразовых функций.

В вашем конкретном примере я бы, вероятно, не использовал нибросьте первые подчеркивания и наберите

sort_by_score(identity_scoring, foo)

, что мне кажется наиболее явным.

2 голосов
/ 23 февраля 2011

Как небольшая касательная, обычно желательно, чтобы встроенная функция sorted выполняла столько же работ по декорированию, сортировке и декорированию, сколько возможно. Например:

def _sort_by_score(scoring, items_list):
    num_items = len(items_list)
    def score(entry):
        return scoring(num_items, entry[0])
    return [item for position, item in sorted(enumerate(items_list), key=score)]

(Размещено только как ответ, потому что блоки кода не работают как комментарии. См. Ответ Свена для ответа на заданный вопрос)

Редактирование кем-то другим : Функция сортировки Python выполняет итерацию по списку и сначала генерирует список ключей. Функция key() вызывается только один раз для каждого элемента списка в порядке ввода списка. Таким образом, вы также можете использовать следующую реализацию:

def _sort_by_score(scoring, items_list):
    num_items = len(items_list)
    index = itertools.count()
    def score(entry):
        return scoring(num_items, next(index))
    return sorted(items_list, key=score)

(Публикуется только как ревизия, потому что блоки кода не работают как комментарии.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...