使用str.startswith的Python高阶函数用法

时间:2015-02-24 11:56:34

标签: python higher-order-functions

我有一个文件,我想清理注释行。 我想使用python functools.partial进行操作,类似于以下方式:

from functools import partial

f = open(filetoread, "r")
lines = f.readlines()
f.close()

# Filtering the comment lines
f_func = partial(str.startswith, prefix="#")
lines = filter(f_func, lines)

这不起作用,显然是因为str.startswith是一种类方法。将functools.partialstr.startswith一起使用的正确方法是什么,以便它可以使用?

2 个答案:

答案 0 :(得分:5)

你有三个问题:

首先,str.startswith不接受关键字参数,只接受位置参数。它在C中实现,并使用PyArg_ParseTuple来获取其位置参数。没有什么可以告诉Python他们有什么名字,因为名称prefix只出现在docstring中。所以prefix=并不好。问题类似于:

>>> str.startswith('#hi', prefix='#')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: startswith() takes no keyword arguments
>>> def foo(self,prefix,start=None,end=None): return str.startswith(self,prefix,start,end)
...
>>> foo('#hi', prefix='#')
True

其次,str.startswith的第一个参数不是前缀,它是self,我们正在检查它是否具有前缀的对象。作为一种方法没有固有的问题,如果有一个名为str.isprefixof的方法,或者如果这个方法是用Python实现的,你会没事的。但是没有,也没有。

最后,functools.partial只允许你绑定初始位置参数,它不知道如何绑定第二个而不是第一个。由于您要绑定的参数不是第一个且无法通过名称访问,因此您没有选项。

但是,这种情况有一种解决方法可以编写功能样式的代码。您只需要operator.methodcaller,而不是functools.partial

f_func = operator.methodcaller('startswith', '#')

operator.methodcaller绑定所有参数,除了第一个。


因为你问的是使用functools的正确方法是什么,我想你可以使用额外的高阶函数来反转参数:

def flip(func):
    return lambda x,y: func(y,x)

f_func = partial(flip(str.startswith), '#')

你可能会认为这是“作弊”,因为它使用了lambda,如果我们想使用lambda,我们就会像Daniel的回答一样使用它。但是我们正在使用它来实现Python没有提供的基本高阶函数,并且一旦这样,定义f_func的行感觉很好并且功能正常。

请注意,我没有在Python术语中实现高阶函数,特别是,因为我没有像partial那样提供任何元数据。它具有func只读属性,用户可能也希望flip能够提供该属性。

或者,您可以编写一个Python包装器到str.startswith,如上面的foo,并将partial应用于此。

答案 1 :(得分:1)

我不认为偏在这里是正确的工具。相反,你可以使用lambda:

func = lambda s: s.startswith('#')
lines = filter(func, lines)

虽然注意到Python并不是一种真正的函数式语言,而更多Pythonic的方法是使用列表理解:

lines = [line for line in lines if line.startswith('#')]