Использование разрыва в понимании списка

Как я могу прервать понимание списка на основе условия, например, когда найдено число 412?

Код:

numbers = [951, 402, 984, 651, 360, 69, 408, 319, 601, 485, 980, 507, 725, 547, 544,
           615, 83, 165, 141, 501, 263, 617, 865, 575, 219, 390, 984, 592, 236, 105, 942, 941,
           386, 462, 47, 418, 907, 344, 236, 375, 823, 566, 597, 978, 328, 615, 953, 345, 399,
           162, 758, 219, 918, 237, 412, 566, 826, 248, 866, 950, 626, 949, 687, 217, 815, 67,
           104, 58, 512, 24, 892, 894, 767, 553, 81, 379, 843, 831, 445, 742, 717, 958, 609, 842,
           451, 688, 753, 854, 685, 93, 857, 440, 380, 126, 721, 328, 753, 470, 743, 527]

even = [n for n in numbers if 0 == n % 2]

Итак, функционально, вы можете сделать вывод, что это должно делать:

even = [n for n in numbers if 0 == n % 2 and break if n == 412]

Я действительно предпочитаю:

  • однострочник
  • никаких других причудливых библиотек, таких как itertools, «чистый python», если это возможно (читай: решение не должно использовать какой-либо оператор import или подобное)

person Flavius    schedule 05.03.2012    source источник
comment
itertools это чистый питон.   -  person Marcin    schedule 05.03.2012
comment
Оба условия не могут быть выполнены одновременно.   -  person Ignacio Vazquez-Abrams    schedule 05.03.2012
comment
... itertools - это Python... В целом это похоже на работу для обычного цикла for.   -  person Felix Kling    schedule 05.03.2012
comment
Почему ограничение на ввоз?   -  person Marcin    schedule 05.03.2012
comment
@Marcin Хороший вопрос! Потому что я хотел, чтобы Python продемонстрировал свои возможности.   -  person Flavius    schedule 05.03.2012
comment
@Flavius: Почему импорт чего-либо из собственной библиотеки Python не демонстрирует свои возможности?   -  person Steven Rumbalski    schedule 05.03.2012
comment
Потому что спорили бы о стандартной библиотеке, а не о самом языке бла-бла.   -  person Flavius    schedule 06.03.2012
comment
@Flavius: они будут спорить [...] это домашняя работа?   -  person Rik Poggi    schedule 06.03.2012
comment
Нет, это не так. это некоторые коллеги, которых я пытался убедить. Почему всегда видят в людях плохое? Хм!   -  person Flavius    schedule 06.03.2012
comment
@Flavius: Итак, вы пытаетесь убедить своих коллег, что вы можете написать что-то хакерское и уродливое на Python, и это как-то их впечатлит?   -  person Steven Rumbalski    schedule 06.03.2012
comment
Нет, этот питон сильнее, чем они думают.   -  person Flavius    schedule 06.03.2012
comment
@Flavius: Нет ничего плохого или плохого, если бы это было домашнее задание, просто тег, чтобы сообщить людям, что это такое и почему у него могут быть такие странные требования.   -  person Rik Poggi    schedule 06.03.2012
comment
@Flavius, большая часть силы Python заключается в его стандартных библиотеках. Был бы Python более мощным, если бы вместо этого было больше встроенных функций стандартной библиотеки? Думаю, нет. Они стандартны в любом случае, но было бы больше возможностей для конфликтов пространств имен, если бы они были встроенными. Лучше их разделить.   -  person senderle    schedule 19.03.2012


Ответы (8)


even = [n for n in numbers[:None if 412 not in numbers else numbers.index(412)] if not n % 2] 

Просто взял код F.J. выше и добавил тройку, чтобы проверить, есть ли 412 в списке. Все еще «однострочный» и будет работать, даже если 412 нет в списке.

person Michael David Watson    schedule 28.11.2012
comment
Если 412 не находится в numbers, последний элемент теряется, если он четный. - person Reinstate Monica; 06.06.2015
comment
Кто такой FJ и где приведенный выше код? Не путайте Stack Overflow с (обычным) форумом; согласно туру, ответы могут подниматься вверх или опускаться вниз. - person Jongware; 09.03.2018
comment
@usr2564301. Я запомню это. Когда я ответил на этот вопрос 6 лет назад, должен был быть другой ответ, который был оценен выше этого, и я его улучшил. - person Michael David Watson; 10.03.2018
comment
Вы все еще помните, в какой ответ вы внесли поправку? :) - person Jongware; 10.03.2018
comment
@usr2564301 usr2564301 Я думаю, что это должен быть этот stackoverflow.com/a/9572876/4458173 - person Angelo Cardellicchio; 06.03.2019
comment
Спасибо всем, кто поделился советами в этом посте. Однако такая практика просто расстраивает менее опытных людей, пытающихся разобраться в этом 12 месяцев спустя. Можем ли мы избежать однострочников, которые приводят к часовой попытке их декодирования? - person Felipe Alvarez; 11.02.2020

Используйте функцию, чтобы поднять StopIteration и list, чтобы поймать его:

>>> def end_of_loop():
...     raise StopIteration
... 
>>> even = list(end_of_loop() if n == 412 else n for n in numbers if 0 == n % 2)
>>> print(even)
[402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418, 344, 236, 566, 978, 328, 162, 758, 918]

Для тех, кто жалуется, это не однострочный:

even = list(next(iter(())) if n == 412 else n for n in numbers if 0 == n % 2)

Для тех, кто жалуется, что это хакерство и не должно использоваться в рабочем коде: Ну, вы правы. Определенно.

person Reinstate Monica    schedule 05.03.2012
comment
Насколько я понимаю, это не понимание списка, НО это однострочный (я буду считать его таковым, поскольку функция используется только из-за необъяснимого ограничения в python для этих операторов raise), и это не вроде есть недостатки. +1ред. - person Flavius; 05.03.2012
comment
Интересный трюк! Не то чтобы я когда-либо использовал это в реальном коде, но в любом случае это приятное наблюдение. - person Sven Marnach; 05.03.2012
comment
+0. Умный. Но хакерский, а не однострочный. - person Steven Rumbalski; 05.03.2012
comment
Или для однострочника замените end_of_loop() на next(iter([])). - person Andrew Clark; 06.03.2012
comment
@ F.J Да, я обновил свой ответ за несколько минут до вашего комментария. - person Reinstate Monica; 06.03.2012
comment
Ха, +1 - это довольно умно :) - person RocketDonkey; 04.12.2012
comment
Есть ли кошерное исправление в Python 3+? - person Zuza; 08.11.2018
comment
@Zuza Очень похожий пример включен в Примеры PEP поломка раздел. Вы можете найти его в конце. Однострочный обходной путь больше невозможен — вам нужно определить функцию генератора. - person Sherpa; 07.02.2019

Вы можете использовать выражения генератора вместе с itertools.takewhile():

even_numbers = (n for n in numbers if not n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))

Изменить: я только что заметил требование не использовать import. Ну, я все равно оставлю этот ответ здесь.

person Sven Marnach    schedule 05.03.2012
comment
И если он действительно хочет свой единственный лайнер: [n for n in itertools.takewhile(lambda x: x != 412, numbers) if not n % 2] - person Steven Rumbalski; 05.03.2012

Я знаю, что это ОЧЕНЬ СТАРЫЙ пост, однако, поскольку OP спросил об использовании break внутри list-comprehension, и я также искал что-то подобное, я решил опубликовать свои выводы здесь для дальнейшего использования.

Исследуя break, я наткнулся на малоизвестную особенность iter как iter(callable, sentinel), которая возвращает итератор, который "ломает" итерацию, как только вызываемое значение function равно значению sentinel.

>>> help(iter)
Help on built-in function iter in module __builtin__:

iter(...)
    iter(collection) -> iterator
    iter(callable, sentinel) -> iterator

    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.

Сложность здесь заключается в определении функции, которая подойдет для данной задачи. В этом случае сначала нам нужно преобразовать заданное list из numbers в iterator, используя x = iter(numbers), которое подается как внешняя переменная в функцию lambda.

Далее, наша вызываемая функция — это просто вызов итератора для выдачи следующего значения. Затем итератор сравнивает с нашим дозорным значением (в данном случае 412) и «ломается» при достижении этого значения.

print [i for i in iter(lambda x=iter(numbers): next(x),412) if i %2 == 0]

>>> 
[402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418,  
 344, 236, 566, 978, 328, 162, 758, 918]
person Anil_M    schedule 06.10.2017
comment
Можете ли вы остановить его после достижения значения, а не до? - person Elliptica; 22.06.2018
comment
Я так не думаю. Эта схема по определению прерывает итерацию, как только значение вызываемой функции становится равным сигнальному значению. Возможно, вы захотите взглянуть на решение @ WolframH и изменить функцию в соответствии с вашими потребностями. - person Anil_M; 25.06.2018

Если 412 определенно будет в списке, вы можете использовать это:

even = [n for n in numbers[:numbers.index(412)] if not n % 2]

Если вы хотите включить 412 в результат, просто используйте numbers[:numbers.index(412)+1] для среза.

Обратите внимание, что из-за среза это будет менее эффективно (по крайней мере, с точки зрения памяти), чем решение для цикла itertools или for.

person Andrew Clark    schedule 05.03.2012
comment
Мало того, что из-за среза он менее эффективен, он также должен выполнять линейный поиск по списку. - person Felix Kling; 05.03.2012
comment
@FelixKling - Тем не менее, быстрый тест timeit с другими ответами показывает, что это быстрее для предоставленных образцов данных. Я определенно ожидаю, что другие пройдут его по мере увеличения набора данных. - person Andrew Clark; 05.03.2012
comment
@FelixKling: линейный поиск 412 — это сверхбыстрый код C, в то время как другие решения проверяют 412 в коде Python, некоторые решения вызывают функцию (что дорого в CPython) для каждого числа. Я уверен, что это решение быстрее! -- Копирование списка также выполняется в очень быстром коде C; если у вас мало памяти, проблем с производительностью быть не должно. (Хорошо, если первое число — 412, а в списке 10**6 записей, это плохо, но если 412 — последнее число, это решение все равно должно быть очень, очень быстрым.) - person Reinstate Monica; 06.03.2012
comment
Учитывая предыдущий комментарий, +1ed. - person Flavius; 06.03.2012

Синтаксис для отображения списков (включая понимание списков) приведен здесь: http://docs.python.org/reference/expressions.html#list-displays

Как видите, специального синтаксиса while или until нет. Самое близкое, что вы можете получить, это:

even_numbers = (n for n in numbers if 0 == n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))

(Код взят из ответа Свена Марнаха, опубликованного, пока я печатал это).

person Marcin    schedule 05.03.2012
comment
Я не минусовал, но предполагаю, что это потому, что вы взяли код у кого-то другого. По крайней мере, ты это признал. Я проголосую за это. - person CoffeeRain; 05.03.2012

еще одно хитрое однострочное решение для решения breaking in list comprehension с помощью условия end.

без использования numbers.index(412), может быть, немного быстрее?

even = [n for end in [[]] for n in numbers
        if (False if end or n != 412 else end.append(42))
        or not end and not n % 2]

Примечание: это плохая идея. просто для удовольствия : )

как сказал @WolframH:

Для тех, кто жалуется, что это хакерство и не должно использоваться в рабочем коде: Ну, вы правы. Определенно.

person recnac    schedule 14.04.2019

Учитывая, что решение генератора устарело, я придумал следующее:

even = [n for n in next((numbers[:i] for i, n in enumerate(numbers) if n == 412)) if not n % 2]

Затем я вернулся и увидел ответ Эндрю Кларка, который такой же, но намного лучше.

even = [n for n in numbers[:numbers.index(412)] if not n % 2]

Независимо от того, что лучше всего в решении для нарезки, вы можете включить или исключить ряд элементов по обе стороны от конечного элемента, например, чтобы получить 412 и число после:

even = [n for n in numbers[:numbers.index(412)+2] if not n % 2]
person Alex Cooper    schedule 18.06.2021