In Python, partial function application (currying) versus explicit function definition

坚强是说给别人听的谎言 提交于 2020-07-04 10:31:32

问题


In Python, is it considered better style to:

  • explicitly define useful functions in terms of more general, possibly internal use, functions; or,
  • use partial function application to explicitly describe function currying?

I will explain my question by way of a contrived example.

Suppose one writes a function, _sort_by_scoring, that takes two arguments: a scoring function and a list of items. It returns a copy of the original list sorted by scores based on each item's position within the original list. Two example scoring functions are also provided.

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

The function _sort_by_score is never called directly; instead, it is called by other single-argument functions that pass a scoring function and their lone argument (a list of items) to _sort_by_scoring and return the result.

# 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)

Obviously, this intent is better expressed in terms of function currying.

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

Usage (in either case):

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

Apparent advantages of the explicit function definition style:

  1. useful functions may be defined before the more general functions are, without raising NameErrors;
  2. helper functions (e.g., scoring functions) could be defined within the function definition body;
  3. possibly easier to debug;
  4. code looks nice by virtue of "explicit is better than implicit."

Apparent advantages of curried function definition style:

  1. expresses intent of functional programming idiomatically;
  2. code looks nice by virtue of succinctness.

For defining "useful" functions, which of the two styles is preferred? Are there other styles that are more idiomatic/Pythonic/etc.?


回答1:


If you want to have the curried functions as part of a public interface, use explicit function definitions. This has the following additional advantages:

  1. It is easier to assign a docstring to an explicit function definition. For partial() functions, you would have to assign to the __doc__ attribute, which is somewhat ugly.

  2. Real function definitions are easier to skim when browsing the module source.

I would use functools.partial() in a similar way to lambda expressions, i.e. for locally needed throw-away functions.

In your particular example, I'd probably use neither, drop the leading underscores and call

sort_by_score(identity_scoring, foo)

which seems the most explicit to me.




回答2:


As a slight tangent, it's generally desirable to let the sorted builtin do as much the decorate-sort-undecorate work as is practical. For example:

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)]

(Only posted as an answer because blocks of code don't work as comments. See Sven's response for an answer to the actual question asked)

Edit by someone else: The Python sort function iterates through the list and generates the list of keys first. The key() function is called only once for each list item, in the order of the input list. Thus, you can also use the following implementation:

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)

(Only posted as a revision because blocks of code don't work as comments.)



来源:https://stackoverflow.com/questions/5091528/in-python-partial-function-application-currying-versus-explicit-function-defi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!