为什么Python的filter
设计为如果你运行filter(my_predicate, some_set)
,我会返回一个list
对象返回而不是set
个对象?
是否存在 希望结果为set
......的实际情况?
答案 0 :(得分:5)
你可以做一套理解。
{my_predicate(x) for x in some_set} # mapping
{x for x in some_set if my_predicate(x)} # filtering
,例如
In [1]: s = set([1,2,3])
In [2]: {x%2 for x in s}
Out[2]: {0, 1}
Python 2 中的许多“功能”函数都标准化为list
作为输出类型。这只是很久以前的API选择。在itertools
中,许多相同的“功能”功能标准化了提供一个生成器,您可以从中填充您想要的任何数据结构。在Python 3中,它们标准化了提供迭代器。
但是请注意,Python中的“过滤”与其他一些语言不同,例如Haskell。它不被认为是在数据结构的上下文中的转换,并且您不选择通过使它们成为Functor(或其他任何其他)的实例来“赋予”您的数据结构“过滤性”其他语言也有类似的想法。)
因此,Python中的一个常见用例是:“这是一个集合,但我只想要回到所有小于5的值。我不关心他们的'set-ness'之后那一点因为我只是要对它们做一些其他工作,所以就给我一个____。“不需要为保留最初生存的值的上下文而疯狂。
在动态打字文化中,这是非常合理的。但是在静态打字文化中,在转换期间保留类型可能很重要,这会有点令人沮丧。从Python的特定角度来看,它实际上只是一种启发式。
如果它真的只是在set
或tuple
的非常狭窄的上下文中,那么我可能会写一个辅助函数:
def type_preserving_filter(predicate, data):
return type(data)(filter(predicate, data))
,例如
>>> type_preserving_filter(lambda x: x > 3, set([1,2,3,4,5,6,7,7]))
{4, 5, 6, 7}
>>> type_preserving_filter(lambda x: x > 3, list([1,2,3,4,5,6,7,7]))
[4, 5, 6, 7, 7]
>>> type_preserving_filter(lambda x: x > 3, tuple([1,2,3,4,5,6,7,7]))
(4, 5, 6, 7, 7)
适用于Python 2.10和Python 3.4。在Python 2中,这感觉有点浪费;从Python 3中的迭代器构造更好。
答案 1 :(得分:1)
这不仅限于filter()
。但是在Python 3中已经改变了API,其中filter()
现在返回迭代器而不是列表。引用python文档:
Views And Iterators Instead Of Lists
一些众所周知的API不再返回列表:
...
map()
和filter()
返回迭代器。如果你真的需要一个列表,那么快速解决方案就是list(map(...))
,但更好的解决方法通常是使用列表 理解(特别是当原始代码使用lambda时),或 重写代码,使其根本不需要列表。尤其 棘手的是map()
为函数的副作用调用;该 正确的转换是使用常规for循环(因为创建一个 列表只是浪费)。
Python的作者撰写的这篇文章详细说明了在Python 3中放弃filter()
的原因(但是这并没有发生,因为你可以看到上面的内容,尽管推理仍然很重要。)
The fate of
reduce()
in Python 3000...
我认为放弃
filter()
和map()
是毫无争议的;filter(P, S)
几乎总是更清楚地写成[x for x in S if P(x)]
,这具有最常见用法的巨大优势 涉及比较的谓词,例如x==42
,并定义一个 lambda对于读者来说只需要更多的努力(另外 lambda比列表理解慢。更是如此map(F, S)
成为[F(x) for x in S]
。当然,在很多 例如,你可以使用生成器表达式。