1

我有一个文件,我想从中清除注释行。我想以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所以它会起作用?

4

2 回答 2

6

你有三个问题:

首先,str.startswith不接受关键字参数,它只接受位置参数。它在 C 中实现,并用于PyArg_ParseTuple获取其位置参数。没有什么可以告诉 Python 他们有什么名字,因为名字prefix只出现在文档字符串中。所以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

str.startswith或者,您可以像上面一样为 编写一个 Python 包装器foo,然后应用partial到它。

于 2015-02-24T12:11:01.763 回答
1

我不认为 partial 是正确的工具。相反,您可以使用 lambda:

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

尽管请注意 Python 并不是真正的函数式语言,而更 Pythonic 的方式是使用列表推导:

lines = [line for line in lines if line.startswith('#')]
于 2015-02-24T11:59:08.747 回答