80

给定一个列表,有没有办法获得第一个非 None 值?而且,如果是这样,这样做的pythonic方式是什么?

例如,我有:

  • a = objA.addreses.country.code
  • b = objB.country.code
  • c = None
  • d = 'CA'

在这种情况下,如果 a 是 None,那么我想得到 b。如果 a 和 b 都没有,我想得到 d。

目前我正在做一些类似的事情(((a or b) or c) or d),还有其他方法吗?

4

6 回答 6

155

您可以使用next()

>>> a = [None, None, None, 1, 2, 3, 4, 5]
>>> next(item for item in a if item is not None)
1

如果列表只包含无,它会抛出StopIteration异常。如果您想在这种情况下使用默认值,请执行以下操作:

>>> a = [None, None, None]
>>> next((item for item in a if item is not None), 'All are Nones')
All are Nones
于 2013-08-30T13:06:16.010 回答
23

我认为这是处理一小组值时最简单的方法:

firstVal = a or b or c or d

将始终返回在某些情况下有效的第一个非“Falsey”值(假设您不期望任何可能评估为 false 的值,正如@GrannyAching 在下面指出的那样)

于 2019-03-19T18:49:17.687 回答
18

first_true是Python 3 文档中的一个itertools配方:

def first_true(iterable, default=False, pred=None):
    """Returns the first true value in the iterable.

    If no true value is found, returns *default*

    If *pred* is not None, returns the first item
    for which pred(item) is true.

    """
    # first_true([a,b,c], x) --> a or b or c or x
    # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
    return next(filter(pred, iterable), default)

可以选择实现后一个 recipe 或 import more_itertools,一个附带itertoolsrecipe 的库等等:

> pip install more_itertools

利用:

import more_itertools as mit

a = [None, None, None, 1, 2, 3, 4, 5]
mit.first_true(a, pred=lambda x: x is not None)
# 1

a = [None, None, None]
mit.first_true(a, default="All are None", pred=lambda x: x is not None)
# 'All are None'

为什么要使用谓词?

“第一个非- None”项目与“第一个True”项目不同,例如第一个非-[None, None, 0]在哪里,但它不是第一个项目。谓词允许使用,确保迭代中任何第一次看到的、非无的、错误的项目仍然返回(例如,)而不是默认值。0NoneTruefirst_true0False

a = [None, None, None, False]
mit.first_true(a, default="All are None", pred=lambda x: x is not None)
# 'False'
于 2017-05-12T05:55:43.023 回答
8

当列表中的项目计算成本很高时,例如

first_non_null = next((calculate(x) for x in my_list if calculate(x)), None)

# or, when receiving possibly None-values from a dictionary for each list item:

first_non_null = next((my_dict[x] for x in my_list if my_dict.get(x)), None)

那么您可能希望避免重复计算并简化为:

first_non_null = next(filter(bool, map(calculate, my_list)), None)

# or:

first_non_null = next(filter(bool, map(my_dict.get, my_list)), None)

由于使用了生成器表达式,计算只针对第一项执行,直到生成真值。

于 2019-09-20T10:30:00.960 回答
7

改编自以下内容(如果需要,可以单行):

values = (a, b, c, d)
not_None = (el for el in values if el is not None)
value = next(not_None, None)

这采用第一个非None值,或者返回None

于 2013-08-30T13:06:26.110 回答
0

首先要提一下,SQL中存在这样的函数并且被调用coalesce。在 Python 中没有发现这样的东西,所以我自己用@alecxe 的配方做了一个。

def first_not_none(*values):
    return next((v for v in values if v is not None), None)

在这样的情况下真的很有帮助:

attr = 'title'
document[attr] = first_not_none(cli_args.get(attr), document_item.get(attr),
                                defaults_item.get(attr), '')
于 2021-09-27T12:58:36.310 回答