为什么Python中没有第一个(iterable)内置函数?

2024-04-26 03:48:28 发布

您现在位置:Python中文网/ 问答频道 /正文

我想知道Python内置函数中没有first(iterable)的原因,有点类似于any(iterable)all(iterable)(它可能藏在某个stdlib模块中,但我在itertools中没有看到)。first将执行短路发生器评估,以便避免不必要的(可能是无限数量的)操作;即

def identity(item):
    return item

def first(iterable, predicate=identity):
    for item in iterable:
        if predicate(item):
            return item
    raise ValueError('No satisfactory value found')

这样你可以表达如下内容:

denominators = (2, 3, 4, 5)
lcd = first(i for i in itertools.count(1)
    if all(i % denominators == 0 for denominator in denominators))

显然,在这种情况下不能执行list(generator)[0],因为生成器不会终止。

或者,如果要匹配一堆regex(当它们都具有相同的groupdict接口时非常有用):

match = first(regex.match(big_text) for regex in regexes)

通过避免list(generator)[0]和对正匹配进行短路,可以节省许多不必要的处理。


Tags: inforreturnifdefalliterableitem
3条回答

如果有迭代器,可以调用它的next方法。类似于:

In [3]: (5*x for x in xrange(2,4)).next()
Out[3]: 10

我最近问了一个similar question(它现在被标记为这个问题的副本)。我还担心的是,我希望只使用内置的来解决寻找生成器的第一个真实值的问题。我自己的解决办法是:

x = next((v for v in (f(x) for x in a) if v), False)

例如,查找第一个regexp匹配项(不是第一个匹配模式!)这看起来像这样:

patterns = [ r'\d+', r'\s+', r'\w+', r'.*' ]
text = 'abc'
firstMatch = next(
  (match for match in
    (re.match(pattern, text) for pattern in patterns)
   if match),
  False)

它不会对谓词求值两次(如果只返回模式,则必须这样做),而且在理解中也不会使用类似于局部的hack。

但是它有两个嵌套的生成器,逻辑要求只使用一个。所以一个更好的解决方案是好的。

有一个Pypi package called “first”这样做:

>>> from first import first
>>> first([0, None, False, [], (), 42])
42

下面是返回第一个奇数的方法,例如:

>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1)
7

如果只想从迭代器返回第一个元素,而不管是否为true,请执行以下操作:

>>> first([0, None, False, [], (), 42], key=lambda x: True)
0

它是一个非常小的包:它只包含这个函数,它没有依赖项,它在Python2和3上工作。它是一个单独的文件,所以你甚至不需要安装它就可以使用它。

事实上,这里几乎是整个源代码(从2.0.1版开始,由Hynek Schlawack根据MIT许可证发布):

def first(iterable, default=None, key=None):
    if key is None:
        for el in iterable:
            if el:
                return el
    else:
        for el in iterable:
            if key(el):
                return el
    return default

相关问题 更多 >