16

在Python,它被认为是更好的风格:在Python,部分功能的应用程序(柯里)对明确的函数定义

  • 在更一般的,可能内部使用,功能方面明确定义有用的功能;或者,
  • 使用部分函数应用程序来明确描述函数currying?

我会通过一个人为的例子来解释我的问题。

假设有人写了一个函数_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) 

显然,这个意图可以用函数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) 

使用(在任一情况下):

>>> 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 /等风格吗?

+3

一个侧面说明,柯里和部分应用两种不同的东西。柯里意味着一次转换函数来工作一个参数。所以当你应用第一个参数时,你得到的是一个函数,然后你可以应用第二个参数。部分应用程序只是修复一些参数到函数调用 – PuercoPop 2013-04-12 18:23:42

回答

13

如果您想将curried函数作为公共接口的一部分,请使用显式函数定义。这具有以下附加优点:

  1. 将文档字符串分配给显式函数定义更容易。对于partial()函数,您必须分配给__doc__属性,这有点难看。

  2. 浏览模块源时,实际功能定义更容易浏览。

我会以类似于lambda表达式的方式使用functools.partial(),即用于本地需要的丢弃函数。

在你的具体的例子,我可能会使用没有,删除前导下划线,并呼吁

sort_by_score(identity_scoring, foo) 

这似乎是最明确的给我。

+0

而且...我可以放弃我的答案,因为Sven打我:) – ncoghlan 2011-02-23 13:50:51

+1

@ncoghlan:我们两天前没有讨论过类似的东西吗? :) – 2011-02-23 13:52:31

+0

同意!谢谢。 – 2011-02-24 03:47:36

2

作为一个细微的切线,通常需要让内建内容尽可能多地做装饰 - 分类 - 未打稿的工作。例如:

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) 

(仅作为发布修订,因为代码块不作为注释工作。)

+0

这当然更优雅。但是,我有一个更普通的情况,其中_sort_by_score在items_list(称为piles_list)列表上运行,相应的评分函数采用四个参数:piles_list_size,pile_position,items_list_size,item位置。在这种情况下,如何使用排序的键并不明显。也许这是另一次的问题。 =)感谢您的洞察! – 2011-02-24 03:57:55

+0

@Sven:有趣的把戏,我从来没有想到过!现在我想知道,如果语言定义保证了这种行为... – ncoghlan 2011-02-24 05:53:43

+1

[[sorted]的文档(http://docs.python.org/library/functions.html#sorted)至少声明了调用key函数每件商品只有一次。由于这必须在实际排序之前完成,我无法想象这是如何改变的,但当然不能保证。 – 2011-02-24 09:18:01