Идиома Python для возврата первого элемента или None - PullRequest
216 голосов
/ 12 декабря 2008

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

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

Ответы [ 22 ]

1 голос
/ 16 декабря 2008

Из любопытства я проверил время на двух решениях. Решение, которое использует оператор 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.
0 голосов
/ 25 октября 2016
if mylist != []:

       print(mylist[0])

   else:

       print(None)
0 голосов
/ 12 декабря 2008
try:
    return a[0]
except IndexError:
    return None
0 голосов
/ 20 июля 2015

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

Лично я нашел пробное и исключающее очиститель стиля, чтобы прочитать

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
0 голосов
/ 28 июня 2015

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

(my_list and my_list[0]) or None

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

0 голосов
/ 04 февраля 2014

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

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

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

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

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

- одна строка

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

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

0 голосов
/ 13 декабря 2008

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

cond and true_expr or false_expr

есть.

list = get_list()
return list and list[0] or None
0 голосов
/ 27 марта 2013

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

0 голосов
/ 16 декабря 2008
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))

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

0 голосов
/ 12 декабря 2008

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

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

...