问题
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:
- useful functions may be defined before the more general functions are, without raising NameErrors;
- helper functions (e.g., scoring functions) could be defined within the function definition body;
- possibly easier to debug;
- code looks nice by virtue of "explicit is better than implicit."
Apparent advantages of curried function definition style:
- expresses intent of functional programming idiomatically;
- 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:
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.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