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

23 ответов


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

лучший способ это:

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

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

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

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

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

кстати, я не использовал переменную list, потому что это перезаписывает builtin


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

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

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

iter(the_list).next() посмотреть итератор.next () Python 2.2+


ФП почти нет, есть только несколько вещей, чтобы сделать его более подходящие для Python.

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

if list:

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

так Давайте изменим это на

some_list = get_list()
if some_list:

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

def does_nothing():
    pass

foo = does_nothing()
print foo

Если вам нужно вернуть не прекращать функция рано, ненужно явно нет. Довольно кратко, просто верните первую запись, если она существует.

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)

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


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

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

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


for item in get_list():
    return item

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

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

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


Так, вот еще одна возможность.

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

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

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

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

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


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

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", который затем будет доступен :

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.) Тогда builtin 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

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

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.

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


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

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


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

(my_list and my_list[0]) or None

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


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

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


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

используя и-или трюк:

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

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


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

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

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

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

плюсы:

-одна строка

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

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


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

лично я нашел try and except style cleaner для чтения

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

if mylist != []:

       print(mylist[0])

   else:

       print(None)

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

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

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

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


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

cond and true_expr or false_expr

ie.

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