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

473   23  

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, потому что это перезаписывает встроенный

самый идиоматический способ python-использовать следующий () на итераторе, так как список 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() посмотреть итератор.следующий() в 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

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

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

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 нет, без использования try/except или присваивания. Насколько мне известно, ни одна из реализаций выше не может справиться с этой возможностью

падений: 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]

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

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() после

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

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

лично я нашел попробовать и кроме стиля чище читать

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.

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

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

cond and true_expr or false_expr

ie.

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

Comments

    Ничего не найдено.