Получение первого значения None из списка
Учитывая список, есть ли способ получить первое значение, отличное от None? И если да, то каков был бы питонический способ сделать это?
Например, у меня есть:
-
a = objA.addreses.country.code
-
b = objB.country.code
-
c = None
-
d = 'CA'
В этом случае, если a - None, то я хотел бы получить b. Если a и b оба None, я хотел бы получить d.
В настоящее время я делаю что-то по строкам (((a or b) or c) or d)
, есть ли другой способ?
Ответы
Ответ 1
Вы можете использовать next()
:
>>> a = [None, None, None, 1, 2, 3, 4, 5]
>>> next(item for item in a if item is not None)
1
Если список содержит только Nones, он генерирует исключение StopIteration
. Если вы хотите иметь значение по умолчанию в этом случае, сделайте следующее:
>>> a = [None, None, None]
>>> next((item for item in a if item is not None), 'All are Nones')
All are Nones
Ответ 2
first_true
- это рецепт itertools
найти в документации по Python 3:
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)
Можно выбрать реализацию последнего рецепта или импортировать more_itertools
, библиотеку, которая поставляется с рецептами itertools
и многое другое:
> 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'
Зачем использовать предикат?
Элемент "First non- None
" не совпадает с элементом "First True
", например, [None, None, 0]
где 0
- это первый non- None
, но это не первый элемент True
. Предикат позволяет использовать first_true
, гарантируя, что любой первый увиденный, non- Нет, ложный элемент в итерируемом элементе все еще возвращается (например, 0
, False
) вместо значения по умолчанию.
a = [None, None, None, False]
mit.first_true(a, default="All are None", pred=lambda x: x is not None)
# 'False'
Ответ 3
Адаптироваться к следующему (вы можете сделать однострочное изображение, если хотите):
values = (a, b, c, d)
not_None = (el for el in values if el is not None)
value = next(not_None, None)
Это принимает первое значение non None
или вместо этого возвращает None
.
Ответ 4
Я думаю, что это самый простой способ при работе с небольшим набором значений (также будет работать при понимании списка):
firstVal = a or b or c or d
Всегда будет возвращать первое не "Ложное" значение, которое работает в некоторых случаях (учитывая, что вы не ожидаете каких-либо значений, которые могут быть оценены как ложные, как указано @GrannyAching ниже)
Ответ 5
Когда элементы в вашем списке являются дорогостоящими для расчета, например, в
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, (calculate(x) for x in my_list)), None)
# or:
first_non_null = next(filter(bool, (my_dict.get(x) for x in my_list)), None)