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

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

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

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

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

Edit:

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

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, и я думал, что писать функцию было глупо, если бы было простое выражение, которое могло бы сделать трюк. Но, видя эти ответы, кажется, что функция является простым решением.

Ответ 1

Python 2.6 +

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

Ответ 2

Лучший способ:

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

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

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

Ответ 3

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

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

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

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

Ответ 4

Самый идиоматический способ 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+

Ответ 5

Решение 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, который вы ищете.

Ответ 6

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

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

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

Ответ 7

for item in get_list():
    return item

Ответ 8

Для этого есть еще одна возможность.

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

Преимущество: Этот метод обрабатывает случай, когда get_list - None, без использования try/except или assign. Насколько мне известно, ни одна из вышеперечисленных реализаций не может справиться с этой возможностью.

крушения: get_list() вызывается дважды, совершенно ненужно, особенно если список длинный и/или создан при вызове функции.

По правде говоря, это больше "Pythonic", на мой взгляд, для обеспечения кода, который читабельен, чем для создания однострочного интерфейса только потому, что вы можете:) Я должен признать, что я виноват во многих случаях ненужного уплотнения Python код просто потому, что меня так впечатлило, насколько мал я могу сделать сложную функцию:)

Изменить: Как уже упоминалось пользователем "hasen j", условное выражение выше является новым в Python 2.5, как описано здесь: https://docs.python.org/whatsnew/2.5.html#pep-308. Спасибо, hasen!

Ответ 9

Идиома Python для возврата первого элемента или 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]

slice 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

Ответ 10

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

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

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

Ответ 11

Из любопытства я провел тайминги по двум решениям. Решение, которое использует оператор 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.

Ответ 12

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

(my_list and my_list[0]) or None

Примечание.. Это должно хорошо работать для списков объектов, но может привести к неправильному отвечу в случае номера или списка строк в комментариях ниже.

Ответ 13

try:
    return a[0]
except IndexError:
    return None

Ответ 14

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))

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

Ответ 15

Что касается идиом, существует рецепт 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 масштабируются в общем случае для любого итеративного, а не только для списков.

Ответ 16

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

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

Ответ 17

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

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

Ответ 18

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

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

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

Я создал эту ошибку в своем собственном коде слишком много раз!

Ответ 19

А как насчет: next(iter(get_list()), None)? Не может быть самым быстрым здесь, но является стандартным (начиная с Python 2.6) и кратким.

Ответ 20

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

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

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

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

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

-one line

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

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

Ответ 21

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

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

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

Ответ 22

if mylist != []:

       print(mylist[0])

   else:

       print(None)

Ответ 23

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

cond and true_expr or false_expr

т.

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