Я уверен, что есть более простой способ сделать это, что мне не приходит в голову.
Я вызываю кучу методов, возвращающих список. Список может быть пустым. Если список не пустой, я хочу вернуть первый элемент; в противном случае я хочу вернуть None. Этот код работает:
my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None
Мне кажется, что для этого должна быть простая однострочная идиома, но, хоть убей, я не могу этого придумать. Здесь?
Редактировать:
Причина, по которой я ищу здесь однострочное выражение, не в том, что мне нравится невероятно сжатый код, а в том, что мне приходится писать много такого кода:
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, и я думал, что писать функцию было бы глупо, если бы было простое выражение, которое могло бы помочь. Но, глядя на эти ответы, функция является кажется простым решением.
Я думаю, вы имеете в виду next(iter(your_list)), поскольку, если вы предоставляете второй аргумент для iter, вы говорите ему, что первый аргумент вызывается.
Нет, None здесь второй параметр для next(), а не iter(). next() возвращает свой второй параметр, если он задан, вместо повышения StopIteration, если your_list пуст.
Решение, предложенное @ J.F.Sebastian, существует с повторяющимся вопросом: stackoverflow.com/a/18533669/144408
Не нужно извиняться за свой вопрос, это вопрос многих людей. Мне нравится лаконичный код. Все мы делаем. Python известен этим. Это часть того, что делает его более читаемым и быстрым для написания.






(get_list() or [None])[0]
Это должно сработать.
Кстати, я не использовал переменную list, потому что она перезаписывает встроенную функцию list().
Обновлено: раньше у меня была более простая, но неправильная версия.
Это умно и работает, но я думаю, что "return foo [0] if foo else None", как предлагает efotinis, немного легче выполнять для обслуживания.
Вопрос заключался в однострочном решении, поэтому я исключил это.
Ни одно из этих выражений не работает, если get_list () возвращает None; вы получите «TypeError: объект 'NoneType' не подлежит подписке». или «TypeError: неподдерживаемые типы операндов для +: 'NoneType' и 'list'».
В вопросе говорится, что методы возвращают списки, из которых None не входит. Итак, учитывая требования, я по-прежнему считаю, что они оба работают.
Должно работать сейчас, если get_list () возвращает None, поскольку он больше не подписывается и не добавляется.
Вы можете использовать Метод извлечения. Другими словами, извлеките этот код в метод, который вы затем вызовете.
Я бы не стал пытаться сжать его намного сильнее, однострочник кажется более трудным для чтения, чем многословная версия. А если вы используете метод извлечения, это всего лишь один лайнер;)
Лучше всего это:
a = get_list()
return a[0] if a else None
Вы также можете сделать это в одну строку, но программисту гораздо сложнее прочитать:
return (get_list()[:1] or [None])[0]
Ой. Следует упомянуть, что это приложение Python 2.4.
@Adam: будущее может использовать next(iter(your_list), None)
@ J.F.Sebastian на самом деле next(iter(your_list)) достаточно
@BradleagheJohnson: неправильно. Он вызывает StopIteration вместо того, чтобы возвращать None, как запрашивает OP. Попробуйте: next(iter([])).
try:
return a[0]
except IndexError:
return None
Исключения обычно не используются для управления потоком.
Я не думаю, что делать этот код длиннее и добавлять в него обработку исключений - это именно та идиома, которую я искал.
Но это обычная идиома Python! По соображениям производительности вы можете не использовать его; вы получите лучшую производительность от if len(a) > 0:, но это считается хорошим стилем. Обратите внимание, насколько хорошо это выражает проблему: «попробуйте извлечь заголовок списка, и если это не сработает, верните None». Выполните поиск в Google по запросу "EAFP" или посмотрите здесь: python.net/~goodger/projects/pycon/2007/idiomatic/…
@steveha Это зависит от ваших данных. Если len (a) обычно> 0, а len (a) <1 - редкое состояние, перехват исключения будет более производительным.
@limscoder, я с тобой согласен. Я просто не вдавался в подробности когда, вы можете не использовать его по соображениям производительности; Я не говорил, что вы никогда не воспользуетесь этим.
for item in get_list():
return item
Это лаконично и красиво.
К сожалению, это не работает; он генерирует исключение «TypeError: 'NoneType' object is not iterable», если get_list () возвращает None.
Чтобы исправить: "для элемента в get_list () или []:"
Вопрос явно предполагает, что get_list возвращает список. len и getitem вызываются по его результату.
не является идиоматическим эквивалентом python тернарным операторам в стиле C
cond and true_expr or false_expr
т.е.
list = get_list()
return list and list[0] or None
Если [0] - это число 0 или пустая строка (или что-то еще, что оценивается как ложное), это вернет None вместо того, что на самом деле [0].
Решение 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, который вы ищете.
Почему в Python зарезервировано слово «список»? Или, что более важно, где говорится, что «список» является резервным словом в Python? Или, что более важно, как вы проверили, что «список» - зарезервированное слово в Python? Учитывая, что я могу объявить переменную с именем «список», это четко, а не зарезервированное слово.
Дело принято. По-настоящему зарезервированные слова в Python можно найти здесь tinyurl.com/424663 Однако рекомендуется рассматривать встроенные функции и типы как зарезервированные. list () фактически создает список. То же самое и с dict, range и т. д.
Я бы объединил последние две строки, исключив временную переменную (если вам не нужен my_list дальше). Это улучшает читаемость.
Это в значительной степени ответ, к которому я стремился.
get_first_item должен принимать параметр default (например, dict.get), который по умолчанию имеет значение None. Таким образом, интерфейс функции явно сообщает, что происходит, если my_list пуст.
Я опубликовал реализацию get_first () в качестве ответа stackoverflow.com/questions/363944/…
Честно говоря, я не думаю, что есть идиома лучше: ваша ясна и лаконична - ничего «лучше» не нужно. Возможно, но это действительно дело вкуса, вы можете заменить if len(list) > 0: на if list: - пустой список всегда будет оцениваться как False.
Кстати, Python - это нет Perl (это не каламбур!), Вам не обязательно использовать самый крутой код. На самом деле, худший код, который я видел на Python, тоже был очень крутым :-) и совершенно неподдерживаемым.
Между прочим, большинство решений, которые я здесь видел, не принимают во внимание, когда list [0] оценивается как False (например, пустая строка или ноль) - в этом случае все они возвращают None, а не правильный элемент.
В Python считается хорошим стилем писать if lst:, а не if len(lst) > 0:. Кроме того, не используйте ключевые слова Python в качестве имен переменных; это заканчивается слезами. Обычно в качестве имени переменной для некоторого списка используется L или lst. Я уверен, что в данном случае вы не хотели предлагать на самом деле использовать list в качестве имени переменной, вы просто имели в виду «какой-то список». И +1 для «когда lst [0] оценивается как Ложь».
Да, я просто сохранил то же имя OP для большей ясности. Но вы определенно правы: всегда нужно проявлять некоторую осторожность, чтобы не переопределить ключевые слова Python.
Использование трюка и / или:
a = get_list()
return a and a[0] or NoneНесколько человек предложили сделать что-то вроде этого:
list = get_list()
return list and list[0] or None
Это работает во многих случаях, но будет работать только в том случае, если list [0] не равен 0, False или пустой строке. Если list [0] равен 0, False или пустой строке, метод неверно вернет None.
I've created this bug in my own code one too many times !
Это должен быть комментарий под ошибочным (-ыми) ответом (-ями), а не отдельный ответ.
next(iter(your_list), None)
Если your_list может быть None:
next(iter(your_list or []), None)
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
Последний вариант - это почти то, что я ищу: он ясен, работает и не требует от меня определения новой функции. Я бы сказал «точно», если бы перерыв как-то не понадобился, потому что риск пропустить его немалый. Но в этом подходе есть доля правды.
О, мне это совсем не нравится. Если какой-либо элемент в списке оценивается как False, это значение отбрасывается и заменяется. Если у вас есть пустая строка "" в списке, она отбрасывается и заменяется пустым списком []. Если у вас 0, также замените на []. Если у вас там есть False, тоже замените. И т. Д. В конкретном случае это может сойти с рук, но это плохая привычка.
@steveha: bool(lst) сообщает нам, является ли len(lst) > 0, что он ничего не говорит нам о том, что содержит lst, например, bool([False]) == True;, поэтому выражение [False] or [] возвращает [False], то же самое для [0] or [] оно возвращает [0].
@RobertRossney: Я добавил yield_first(), чтобы избежать утверждения break.
Возможно, вам лучше использовать кортеж вместо списка?
@ChristopherSmith: список здесь семантически правильный. for-петля подразумевает гомогенную последовательность. См. Кортежи имеют структуру, списки имеют порядок. Не выполняйте микрооптимизацию, если об этом не говорит профайлер.
Я думаю, что версию Python 2.6+ можно улучшить еще больше: next(iter(your_list or [None]))
next(iter(your_list), None) должно хватить.
@ThomasAhle это было правдой, если your_list не может быть None.
@jfs Верно, но вопрос не говорит о том, что список может быть None.
@ThomasAhle, как вы думаете, что означает название list_or_none?
@jfs в первом разделе «Этот код работает» называется просто my_list. Компонент list_to_none помечен как «безусловно можно выполнить с помощью». Просто скажу, что для OP и большинства людей, задающих этот вопрос, iter(your_list) будет работать нормально.
@ThomasAhle: оба верны. Я обновил ответ, чтобы предоставить решение для случая, отличного от None.
Из любопытства я вычислил тайминги двух решений. Решение, использующее оператор 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.
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))
(Избегайте повторений, когда это возможно)
Наверное, не самое быстрое решение, но об этом варианте никто не упоминал:
dict(enumerate(get_list())).get(0)
если get_list() может вернуть None, вы можете использовать:
dict(enumerate(get_list() or [])).get(0)
Преимущества:
-одна линия
-вы просто позвоните get_list() один раз
-Легко понять
Самый идиоматический способ 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+
Это имеет то преимущество, что вам не нужно назначать свой список переменной, такой как return l[0] if l else None, если список был libcomp. Когда список на самом деле представляет собой genexpr, это даже лучше, поскольку вам не нужно создавать весь список, как в next(iter(x for x in range(10) if x % 2 == 0), None).
Если вы точно знаете, что the_list не пустой, вы должны написать the_list[0].
Как насчет этого:
(my_list and my_list[0]) or None
Примечание: Это должно работать нормально для списков объектов, но может возвращать неправильный ответ в случае числового или строкового списка согласно комментариям ниже.
А как насчет ([0,1] and 0) or None !.
Это хороший момент ... Небезопасно использовать с коллекциями номеров в текущей форме :(
То же самое и с (['','A'] and '') or None.
Если my_list[0] оценивается как False, результатом должен быть None.
Вы должны удалить этот ответ.
my_list[0] if len(my_list) else NonePython idiom to return first item or 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]
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
Мой вариант использования заключался в том, чтобы установить значение локальной переменной.
Лично я нашел, что попробуйте очиститель стилей, кроме того, чтобы прочитать
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
Если вы обнаружите, что пытаетесь извлечь первое (или «Нет») из понимания списка, вы можете переключиться на генератор, чтобы сделать это, например:
next((x for x in blah if cond), None)
Плюсы: работает, если blah не индексируется. Минус: незнакомый синтаксис. Тем не менее, это полезно при взломе и фильтрации материалов в ipython.
if mylist != []:
print(mylist[0])
else:
print(None)
Что касается идиом, существует рецепт 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 масштабируются для любых итераций, а не только для списков.
my_list[0] if len(my_list) else None
Вы используете len без причины. Если бы вы удалили его, приведенный выше код сделал бы то же самое.
Не уверен, насколько это питонично, но пока в библиотеке не появится первая функция, я включаю ее в исходный код:
first = lambda l, default=None: next(iter(l or []), default)
Это всего лишь одна строка (соответствует черному цвету) и позволяет избежать зависимостей.
Заимствование кода more_itertools.first_true дает что-то прилично читаемое:
def first_true(iterable, default=None, pred=None):
return next(filter(pred, iterable), default)
def get_first_non_default(items_list, default=None):
return first_true(items_list, default, pred=lambda x: x!=default)
кстати, на Python 2.6+ вы можете использовать
next(iter(your_list), None)вместоfirst_item(your_list), предполагая, чтоyour_listне являетсяNone(get_first_list()иget_second_list()всегда должны возвращать итерацию).