Идиома 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.
честно говоря, я не думаю, что есть лучшая идиома: ваша ясна и лаконична - нет необходимости в чем-либо "лучше". Может быть, но это действительно вопрос вкуса, вы могли бы изменить 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
Примечание: это должно работать нормально для списков объектов, но может возвращать неправильный ответ в случае списка чисел или строк в комментариях ниже.
можно использовать Извлечение Метода. Другими словами, извлеките этот код в метод, который вы затем вызовете.
Я бы не пытался сжать его намного больше, один лайнеры, кажется, труднее читать, чем многословную версию. И если вы используете метод извлечения, это один лайнер;)
а как же: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
несколько человек предложили сделать что-то вроде этого:
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