Идиома Python для возврата первого элемента или None

Я уверен, что есть более простой способ сделать это, что мне не приходит в голову.

Я вызываю кучу методов, возвращающих список. Список может быть пустым. Если список не пустой, я хочу вернуть первый элемент; в противном случае я хочу вернуть None. Этот код работает:

my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None

Мне кажется, что для этого должна быть простая однострочная идиома, но, хоть убей, я не могу этого придумать. Здесь?

Редактировать:

Причина, по которой я ищу здесь однострочное выражение, не в том, что мне нравится невероятно сжатый код, а в том, что мне приходится писать много такого кода:

x = get_first_list()
if x:
    # do something with x[0]
    # inevitably forget the [0] part, and have a bug to fix
y = get_second_list()
if y:
    # do something with y[0]
    # inevitably forget the [0] part AGAIN, and have another bug to fix

То, что я хотел бы сделать, безусловно, можно выполнить с помощью функции (и, вероятно, так и будет):

def first_item(list_or_none):
    if list_or_none: return list_or_none[0]

x = first_item(get_first_list())
if x:
    # do something with x
y = first_item(get_second_list())
if y:
    # do something with y

Я разместил вопрос, потому что меня часто удивляют, что могут делать простые выражения в Python, и я думал, что писать функцию было бы глупо, если бы было простое выражение, которое могло бы помочь. Но, глядя на эти ответы, функция является кажется простым решением.

кстати, на Python 2.6+ вы можете использовать next(iter(your_list), None) вместо first_item(your_list), предполагая, что your_list не является None (get_first_list() и get_second_list() всегда должны возвращать итерацию).

jfs 13.12.2011 19:57

Я думаю, вы имеете в виду next(iter(your_list)), поскольку, если вы предоставляете второй аргумент для iter, вы говорите ему, что первый аргумент вызывается.

Robert Rossney 18.12.2011 00:53

Нет, None здесь второй параметр для next(), а не iter(). next() возвращает свой второй параметр, если он задан, вместо повышения StopIteration, если your_list пуст.

jfs 18.12.2011 02:59

Решение, предложенное @ J.F.Sebastian, существует с повторяющимся вопросом: stackoverflow.com/a/18533669/144408

shahjapan 24.06.2016 12:35

Не нужно извиняться за свой вопрос, это вопрос многих людей. Мне нравится лаконичный код. Все мы делаем. Python известен этим. Это часть того, что делает его более читаемым и быстрым для написания.

NeilG 23.02.2021 02:42
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
317
5
327 924
24
Перейти к ответу Данный вопрос помечен как решенный

Ответы 24

(get_list() or [None])[0]

Это должно сработать.

Кстати, я не использовал переменную list, потому что она перезаписывает встроенную функцию list().

Обновлено: раньше у меня была более простая, но неправильная версия.

Это умно и работает, но я думаю, что "return foo [0] if foo else None", как предлагает efotinis, немного легче выполнять для обслуживания.

Jay 12.12.2008 23:21

Вопрос заключался в однострочном решении, поэтому я исключил это.

recursive 12.12.2008 23:25

Ни одно из этих выражений не работает, если get_list () возвращает None; вы получите «TypeError: объект 'NoneType' не подлежит подписке». или «TypeError: неподдерживаемые типы операндов для +: 'NoneType' и 'list'».

Robert Rossney 13.12.2008 22:46

В вопросе говорится, что методы возвращают списки, из которых None не входит. Итак, учитывая требования, я по-прежнему считаю, что они оба работают.

recursive 13.12.2008 23:30

Должно работать сейчас, если get_list () возвращает None, поскольку он больше не подписывается и не добавляется.

Robert 02.02.2017 02:51

Вы можете использовать Метод извлечения. Другими словами, извлеките этот код в метод, который вы затем вызовете.

Я бы не стал пытаться сжать его намного сильнее, однострочник кажется более трудным для чтения, чем многословная версия. А если вы используете метод извлечения, это всего лишь один лайнер;)

Лучше всего это:

a = get_list()
return a[0] if a else None

Вы также можете сделать это в одну строку, но программисту гораздо сложнее прочитать:

return (get_list()[:1] or [None])[0]

Ой. Следует упомянуть, что это приложение Python 2.4.

Robert Rossney 13.12.2008 00:47

@Adam: будущее может использовать next(iter(your_list), None)

jfs 06.04.2014 15:02

@ J.F.Sebastian на самом деле next(iter(your_list)) достаточно

Brad Johnson 14.05.2015 20:49

@BradleagheJohnson: неправильно. Он вызывает StopIteration вместо того, чтобы возвращать None, как запрашивает OP. Попробуйте: next(iter([])).

jfs 14.05.2015 21:19
try:
    return a[0]
except IndexError:
    return None

Исключения обычно не используются для управления потоком.

Matt Green 12.12.2008 23:33

Я не думаю, что делать этот код длиннее и добавлять в него обработку исключений - это именно та идиома, которую я искал.

Robert Rossney 13.12.2008 00:26

Но это обычная идиома Python! По соображениям производительности вы можете не использовать его; вы получите лучшую производительность от if len(a) > 0:, но это считается хорошим стилем. Обратите внимание, насколько хорошо это выражает проблему: «попробуйте извлечь заголовок списка, и если это не сработает, верните None». Выполните поиск в Google по запросу "EAFP" или посмотрите здесь: python.net/~goodger/projects/pycon/2007/idiomatic/…

steveha 22.11.2009 07:23

@steveha Это зависит от ваших данных. Если len (a) обычно> 0, а len (a) <1 - редкое состояние, перехват исключения будет более производительным.

limscoder 11.06.2012 01:49

@limscoder, я с тобой согласен. Я просто не вдавался в подробности когда, вы можете не использовать его по соображениям производительности; Я не говорил, что вы никогда не воспользуетесь этим.

steveha 11.06.2012 05:34
for item in get_list():
    return item

Это лаконично и красиво.

gotgenes 13.12.2008 02:17

К сожалению, это не работает; он генерирует исключение «TypeError: 'NoneType' object is not iterable», если get_list () возвращает None.

Robert Rossney 13.12.2008 23:05

Чтобы исправить: "для элемента в get_list () или []:"

itsadok 01.06.2009 11:18

Вопрос явно предполагает, что get_list возвращает список. len и getitem вызываются по его результату.

A. Coady 11.06.2009 04:52

не является идиоматическим эквивалентом python тернарным операторам в стиле C

cond and true_expr or false_expr

т.е.

list = get_list()
return list and list[0] or None

Если [0] - это число 0 или пустая строка (или что-то еще, что оценивается как ложное), это вернет None вместо того, что на самом деле [0].

Adam Rosenfield 13.12.2008 01:00

Решение OP почти готово, есть всего несколько вещей, чтобы сделать его более Pythonic.

Во-первых, нет необходимости получать длину списка. Пустые списки в Python оцениваются как False при проверке if. Просто скажи

if list:

Кроме того, очень плохая идея назначать переменным, которые перекрываются зарезервированными словами. «список» - зарезервированное слово в Python.

Так что давайте изменим это на

some_list = get_list()
if some_list:

Действительно важный момент, который упускают из виду многие решения, - это все функции / методы Python по умолчанию возвращают None. Попробуйте следующее.

def does_nothing():
    pass

foo = does_nothing()
print foo

Если вам не нужно возвращать None для досрочного завершения функции, нет необходимости явно возвращать None. Короче говоря, просто верните первую запись, если она существует.

some_list = get_list()
if some_list:
    return list[0]

И, наконец, возможно, это подразумевалось, но чтобы быть явным (потому что явное лучше, чем неявное), вы не должны позволять вашей функции получать список из другой функции; просто передайте его как параметр. Итак, окончательный результат будет

def get_first_item(some_list): 
    if some_list:
        return list[0]

my_list = get_list()
first_item = get_first_item(my_list)

Как я уже сказал, OP был почти готов, и всего несколько штрихов придают ему оттенок Python, который вы ищете.

Почему в Python зарезервировано слово «список»? Или, что более важно, где говорится, что «список» является резервным словом в Python? Или, что более важно, как вы проверили, что «список» - зарезервированное слово в Python? Учитывая, что я могу объявить переменную с именем «список», это четко, а не зарезервированное слово.

Lasse V. Karlsen 13.12.2008 01:48

Дело принято. По-настоящему зарезервированные слова в Python можно найти здесь tinyurl.com/424663 Однако рекомендуется рассматривать встроенные функции и типы как зарезервированные. list () фактически создает список. То же самое и с dict, range и т. д.

gotgenes 13.12.2008 02:01

Я бы объединил последние две строки, исключив временную переменную (если вам не нужен my_list дальше). Это улучшает читаемость.

Svante 13.12.2008 22:53

Это в значительной степени ответ, к которому я стремился.

Robert Rossney 13.12.2008 23:09
get_first_item должен принимать параметр default (например, dict.get), который по умолчанию имеет значение None. Таким образом, интерфейс функции явно сообщает, что происходит, если my_list пуст.
jfs 14.12.2008 02:21

Я опубликовал реализацию get_first () в качестве ответа stackoverflow.com/questions/363944/…

jfs 14.12.2008 02:33

Честно говоря, я не думаю, что есть идиома лучше: ваша ясна и лаконична - ничего «лучше» не нужно. Возможно, но это действительно дело вкуса, вы можете заменить if len(list) > 0: на if list: - пустой список всегда будет оцениваться как False.

Кстати, Python - это нет Perl (это не каламбур!), Вам не обязательно использовать самый крутой код. На самом деле, худший код, который я видел на Python, тоже был очень крутым :-) и совершенно неподдерживаемым.

Между прочим, большинство решений, которые я здесь видел, не принимают во внимание, когда list [0] оценивается как False (например, пустая строка или ноль) - в этом случае все они возвращают None, а не правильный элемент.

В Python считается хорошим стилем писать if lst:, а не if len(lst) > 0:. Кроме того, не используйте ключевые слова Python в качестве имен переменных; это заканчивается слезами. Обычно в качестве имени переменной для некоторого списка используется L или lst. Я уверен, что в данном случае вы не хотели предлагать на самом деле использовать list в качестве имени переменной, вы просто имели в виду «какой-то список». И +1 для «когда lst [0] оценивается как Ложь».

steveha 22.11.2009 07:15

Да, я просто сохранил то же имя OP для большей ясности. Но вы определенно правы: всегда нужно проявлять некоторую осторожность, чтобы не переопределить ключевые слова Python.

rob 23.11.2009 01:33

Использование трюка и / или:

a = get_list()
return a and a[0] or None

Несколько человек предложили сделать что-то вроде этого:

list = get_list()
return list and list[0] or None

Это работает во многих случаях, но будет работать только в том случае, если list [0] не равен 0, False или пустой строке. Если list [0] равен 0, False или пустой строке, метод неверно вернет None.

I've created this bug in my own code one too many times !

Это должен быть комментарий под ошибочным (-ыми) ответом (-ями), а не отдельный ответ.

I. J. Kennedy 19.05.2012 06:34
Ответ принят как подходящий

Python 2.6+

next(iter(your_list), None)

Если your_list может быть None:

next(iter(your_list or []), None)

Python 2.4

def get_first(iterable, default=None):
    if iterable:
        for item in iterable:
            return item
    return default

Пример:

x = get_first(get_first_list())
if x:
    ...
y = get_first(get_second_list())
if y:
    ...

Другой вариант - встроить указанную выше функцию:

for x in get_first_list() or []:
    # process x
    break # process at most one item
for y in get_second_list() or []:
    # process y
    break

Чтобы избежать break, вы можете написать:

for x in yield_first(get_first_list()):
    x # process x
for y in yield_first(get_second_list()):
    y # process y

Где:

def yield_first(iterable):
    for item in iterable or []:
        yield item
        return

Последний вариант - это почти то, что я ищу: он ясен, работает и не требует от меня определения новой функции. Я бы сказал «точно», если бы перерыв как-то не понадобился, потому что риск пропустить его немалый. Но в этом подходе есть доля правды.

Robert Rossney 16.12.2008 03:56

О, мне это совсем не нравится. Если какой-либо элемент в списке оценивается как False, это значение отбрасывается и заменяется. Если у вас есть пустая строка "" в списке, она отбрасывается и заменяется пустым списком []. Если у вас 0, также замените на []. Если у вас там есть False, тоже замените. И т. Д. В конкретном случае это может сойти с рук, но это плохая привычка.

steveha 22.11.2009 07:10

@steveha: bool(lst) сообщает нам, является ли len(lst) > 0, что он ничего не говорит нам о том, что содержит lst, например, bool([False]) == True;, поэтому выражение [False] or [] возвращает [False], то же самое для [0] or [] оно возвращает [0].

jfs 28.11.2009 02:43

@RobertRossney: Я добавил yield_first(), чтобы избежать утверждения break.

jfs 26.05.2012 23:47

Возможно, вам лучше использовать кортеж вместо списка?

Christopher Smith 20.01.2013 00:12

@ChristopherSmith: список здесь семантически правильный. for-петля подразумевает гомогенную последовательность. См. Кортежи имеют структуру, списки имеют порядок. Не выполняйте микрооптимизацию, если об этом не говорит профайлер.

jfs 20.01.2013 00:36

Я думаю, что версию Python 2.6+ можно улучшить еще больше: next(iter(your_list or [None]))

Andrew Kravchuk 24.04.2018 18:42
next(iter(your_list), None) должно хватить.
Thomas Ahle 22.05.2018 15:59

@ThomasAhle это было правдой, если your_list не может быть None.

jfs 22.05.2018 16:06

@jfs Верно, но вопрос не говорит о том, что список может быть None.

Thomas Ahle 23.05.2018 13:57

@ThomasAhle, как вы думаете, что означает название list_or_none?

jfs 23.05.2018 14:04

@jfs в первом разделе «Этот код работает» называется просто my_list. Компонент list_to_none помечен как «безусловно можно выполнить с помощью». Просто скажу, что для OP и большинства людей, задающих этот вопрос, iter(your_list) будет работать нормально.

Thomas Ahle 23.05.2018 14:09

@ThomasAhle: оба верны. Я обновил ответ, чтобы предоставить решение для случая, отличного от None.

jfs 23.05.2018 14:45

Из любопытства я вычислил тайминги двух решений. Решение, использующее оператор return для преждевременного завершения цикла for, немного дороже на моем компьютере с Python 2.5.1, я подозреваю, что это связано с настройкой итерации.

import random
import timeit

def index_first_item(some_list):
    if some_list:
        return some_list[0]


def return_first_item(some_list):
    for item in some_list:
        return item


empty_lists = []
for i in range(10000):
    empty_lists.append([])

assert empty_lists[0] is not empty_lists[1]

full_lists = []
for i in range(10000):
    full_lists.append(list([random.random() for i in range(10)]))

mixed_lists = empty_lists[:50000] + full_lists[:50000]
random.shuffle(mixed_lists)

if __name__ == '__main__':
    ENV = 'import firstitem'
    test_data = ('empty_lists', 'full_lists', 'mixed_lists')
    funcs = ('index_first_item', 'return_first_item')
    for data in test_data:
        print "%s:" % data
        for func in funcs:
            t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
                func, data), ENV)
            times = t.repeat()
            avg_time = sum(times) / len(times)
            print "  %s:" % func
            for time in times:
                print "    %f seconds" % time
            print "    %f seconds avg." % avg_time

Вот время, которое у меня получилось:

empty_lists:
  index_first_item:
    0.748353 seconds
    0.741086 seconds
    0.741191 seconds
    0.743543 seconds avg.
  return_first_item:
    0.785511 seconds
    0.822178 seconds
    0.782846 seconds
    0.796845 seconds avg.
full_lists:
  index_first_item:
    0.762618 seconds
    0.788040 seconds
    0.786849 seconds
    0.779169 seconds avg.
  return_first_item:
    0.802735 seconds
    0.878706 seconds
    0.808781 seconds
    0.830074 seconds avg.
mixed_lists:
  index_first_item:
    0.791129 seconds
    0.743526 seconds
    0.744441 seconds
    0.759699 seconds avg.
  return_first_item:
    0.784801 seconds
    0.785146 seconds
    0.840193 seconds
    0.803380 seconds avg.
def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

print head(xrange(42, 1000)  # 42
print head([])               # None

Кстати: я бы переделал ваш общий поток программы примерно так:

lists = [
    ["first", "list"],
    ["second", "list"],
    ["third", "list"]
]

def do_something(element):
    if not element:
        return
    else:
        # do something
        pass

for li in lists:
    do_something(head(li))

(Избегайте повторений, когда это возможно)

Наверное, не самое быстрое решение, но об этом варианте никто не упоминал:

dict(enumerate(get_list())).get(0)

если get_list() может вернуть None, вы можете использовать:

dict(enumerate(get_list() or [])).get(0)

Преимущества:

-одна линия

-вы просто позвоните get_list() один раз

-Легко понять

Самый идиоматический способ Python - использовать next () на итераторе, поскольку список повторяемый. точно так же, как @ J.F.Sebastian оставил комментарий 13 декабря 2011 г.

next(iter(the_list), None) Это возвращает None, если the_list пуст. см. next () Python 2.6+

или если вы точно знаете, что the_list не пустой:

iter(the_list).next() см. iterator.next () Python 2.2+

Это имеет то преимущество, что вам не нужно назначать свой список переменной, такой как return l[0] if l else None, если список был libcomp. Когда список на самом деле представляет собой genexpr, это даже лучше, поскольку вам не нужно создавать весь список, как в next(iter(x for x in range(10) if x % 2 == 0), None).

RubenLaguna 11.02.2016 12:25

Если вы точно знаете, что the_list не пустой, вы должны написать the_list[0].

kaya3 23.12.2019 20:37

Как насчет этого:

(my_list and my_list[0]) or None

Примечание: Это должно работать нормально для списков объектов, но может возвращать неправильный ответ в случае числового или строкового списка согласно комментариям ниже.

А как насчет ([0,1] and 0) or None !.

Kenly 08.11.2016 14:53

Это хороший момент ... Небезопасно использовать с коллекциями номеров в текущей форме :(

VitalyB 08.11.2016 14:55

То же самое и с (['','A'] and '') or None.

Kenly 08.11.2016 14:57

Если my_list[0] оценивается как False, результатом должен быть None.

Kenly 08.11.2016 15:17

Вы должны удалить этот ответ.

Robino 13.09.2017 14:17
my_list[0] if len(my_list) else None
Nicholas Hamilton 16.09.2019 17:27

Python idiom to return first item or None?

Самый питонический подход - это то, что продемонстрировал самый популярный ответ, и это было первое, что пришло мне в голову, когда я прочитал вопрос. Вот как его использовать, если, возможно, пустой список передается в функцию:

def get_first(l): 
    return l[0] if l else None

И если список возвращается из функции get_list:

l = get_list()
return l[0] if l else None

Здесь показаны другие способы сделать это с пояснениями.

for

Когда я начал думать об умных способах сделать это, я подумал о следующем:

for item in get_list():
    return item

Это предполагает, что функция здесь заканчивается, неявно возвращая None, если get_list возвращает пустой список. Приведенный ниже явный код в точности эквивалентен:

for item in get_list():
    return item
return None

if some_list

Также было предложено следующее (я исправил неправильное имя переменной), в котором также используется неявный None. Это было бы предпочтительнее, чем описанное выше, поскольку здесь используется логическая проверка вместо итерации, которая может не произойти. Это должно быть проще, чтобы сразу понять, что происходит. Но если мы пишем для удобства чтения и сопровождения, мы также должны добавить явный return None в конце:

some_list = get_list()
if some_list:
    return some_list[0]

нарезать or [None] и выбрать нулевой индекс

Этот ответ также находится в наиболее популярном ответе:

return (get_list()[:1] or [None])[0]

Срез не нужен и создает в памяти дополнительный список из одного элемента. Следующее должно быть более эффективным. Чтобы пояснить, or возвращает второй элемент, если первым является False в логическом контексте, поэтому, если get_list возвращает пустой список, выражение, содержащееся в скобках, вернет список с 'None', к которому затем будет обращаться индекс 0. :

return (get_list() or [None])[0]

Следующий использует тот факт, что и возвращает второй элемент, если первым является True в логическом контексте, и поскольку он дважды ссылается на my_list, это не лучше, чем троичное выражение (и технически не однострочное):

my_list = get_list() 
return (my_list and my_list[0]) or None

next

Затем у нас есть следующее умное использование встроенных next и iter

return next(iter(get_list()), None)

Чтобы объяснить, iter возвращает итератор с методом .next. (.__next__ в Python 3.) Затем встроенный next вызывает этот метод .next, и, если итератор исчерпан, возвращает значение по умолчанию, которое мы даем, None.

избыточное тройное выражение (a if b else c) и возвращение обратно

Было предложено следующее, но обратное было бы предпочтительнее, поскольку логика обычно лучше понимается в положительном, а не в отрицательном смысле. Поскольку get_list вызывается дважды, если результат каким-либо образом не запоминать, это будет плохо работать:

return None if not get_list() else get_list()[0]

Лучшая инверсия:

return get_list()[0] if get_list() else None

Еще лучше, используйте локальную переменную, чтобы get_list вызывался только один раз, и сначала вы обсудили рекомендуемое решение Pythonic:

l = get_list()
return l[0] if l else None

Мой вариант использования заключался в том, чтобы установить значение локальной переменной.

Лично я нашел, что попробуйте очиститель стилей, кроме того, чтобы прочитать

items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item

чем разрезание списка.

items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item

Если вы обнаружите, что пытаетесь извлечь первое (или «Нет») из понимания списка, вы можете переключиться на генератор, чтобы сделать это, например:

next((x for x in blah if cond), None)

Плюсы: работает, если blah не индексируется. Минус: незнакомый синтаксис. Тем не менее, это полезно при взломе и фильтрации материалов в ipython.

if mylist != []:

       print(mylist[0])

   else:

       print(None)

Что касается идиом, существует рецепт itertools под названием nth.

Из рецептов itertools:

def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(islice(iterable, n, None), default)

Если вам нужны однострочники, подумайте об установке библиотеки, которая реализует этот рецепт за вас, например more_itertools:

import more_itertools as mit

mit.nth([3, 2, 1], 0)
# 3

mit.nth([], 0)                                             # default is `None`
# None

Доступен другой инструмент, который возвращает только первый элемент, называемый more_itertools.first.

mit.first([3, 2, 1])
# 3

mit.first([], default=None)
# None

Эти инструменты itertools масштабируются для любых итераций, а не только для списков.

my_list[0] if len(my_list) else None

Вы используете len без причины. Если бы вы удалили его, приведенный выше код сделал бы то же самое.

Daniel Moskovich 26.05.2020 22:23

Не уверен, насколько это питонично, но пока в библиотеке не появится первая функция, я включаю ее в исходный код:

first = lambda l, default=None: next(iter(l or []), default)

Это всего лишь одна строка (соответствует черному цвету) и позволяет избежать зависимостей.

Заимствование кода more_itertools.first_true дает что-то прилично читаемое:

def first_true(iterable, default=None, pred=None):
    return next(filter(pred, iterable), default)

def get_first_non_default(items_list, default=None):
    return first_true(items_list, default, pred=lambda x: x!=default)

Другие вопросы по теме