#python #list #generator #list-comprehension
#python #Список #генератор #понимание списка
Вопрос:
Я нашел это понимание, которое идеально подходит для выравнивания списка списков:
>>> list_of_lists = [(1,2,3),(2,3,4),(3,4,5)]
>>> [item for sublist in list_of_lists for item in sublist]
[1, 2, 3, 2, 3, 4, 3, 4, 5]
Мне это нравится больше, чем использование itertools.chain()
, но я просто не могу этого понять. Я пробовал заключать части в круглые скобки, чтобы посмотреть, смогу ли я уменьшить сложность, но теперь я просто еще больше запутался:
>>> [(item for sublist in list_of_lists) for item in sublist]
[<generator object <genexpr> at 0x7ff919fdfd20>, <generator object <genexpr> at 0x7ff919fdfd70>, <generator object <genexpr> at 0x7ff919fdfdc0>]
>>> [item for sublist in (list_of_lists for item in sublist)]
[5, 5, 5]
У меня такое чувство, что мне трудно понять, потому что я не совсем понимаю, как работают генераторы… Я имею в виду, я думал, что сделал, но теперь я серьезно сомневаюсь. Как я уже сказал, мне нравится, насколько компактна эта идиома, и это именно то, что мне нужно, но я терпеть не могу использовать код, который я не понимаю.
Что именно здесь происходит?
Комментарии:
1. Привет! Изменилось ли что-нибудь, что заставило вас выбрать другой принятый ответ сегодня? Просто пытаюсь понять; знак «принять» — это полностью ваш выбор! Просто немного редко можно увидеть, как это меняется после такого долгого времени 🙂
2. честно говоря, я не уверен? Я думаю, что меня направили сюда от кого-то, получающего какой-то значок SO, и, возможно, я случайно нажал другой знак принятия.
3. Круто, спасибо за ответ! Я всегда стараюсь сделать свои ответы как можно более полезными, и отметка «принять» является хорошим показателем того, попал я в точку или нет. 🙂
Ответ №1:
Понимание списка
Когда я впервые начал с понимания списка, я прочитал это как английские предложения, и я смог легко понять их. Например,
[item for sublist in list_of_lists for item in sublist]
может читаться как
for each sublist in list_of_lists and for each item in sublist add item
Кроме того, часть фильтрации может быть прочитана как
for each sublist in list_of_lists and for each item in sublist add item only if it is valid
И соответствующее понимание было бы
[item for sublist in list_of_lists for item in sublist if valid(item)]
Генераторы
Они похожи на наземные мины, срабатывающие только при вызове с next
протоколом. Они похожи на функции, но до тех пор, пока не возникнет исключение или не будет достигнуто завершение функции, они не исчерпаны и их можно вызывать снова и снова. Важно то, что они сохраняют состояние между предыдущим вызовом и текущим.
Разница между генератором и функцией заключается в том, что генераторы используют yield
ключевое слово для возврата значения вызывающему. В случае выражения генератора они аналогичны пониманию списка, первым выражением является фактическое значение, которое «выдается».
С этим базовым пониманием, если мы посмотрим на ваши выражения в вопросе,
[(item for sublist in list_of_lists) for item in sublist]
Вы смешиваете понимание списка с выражениями генератора. Это будет читаться следующим образом
for each item in sublist add a generator expression which is defined as, for every sublist in list_of_lists yield item
это не то, что вы имели в виду. И поскольку выражение генератора не повторяется, объект выражения генератора добавляется в список как есть. Поскольку они не будут оцениваться без вызова со следующим протоколом, они не выдадут никакой ошибки (если таковые имеются, если в них нет синтаксической ошибки). В этом случае это приведет к ошибке времени выполнения, поскольку sublist
еще не определено.
Кроме того, в последнем случае,
[item for sublist in (list_of_lists for item in sublist)]
for each sublist in the generator expression, add item and the generator expression is defined as for each item in sublist yield list_of_lists.
Цикл for повторит любую итерацию со следующим протоколом. Итак, выражение генератора будет вычислено, и item
всегда будет последним элементом в итерации sublist
, и вы добавляете его в список. Это также приведет к ошибке во время выполнения, поскольку подсписок еще не определен.
Комментарии:
1. Могу ли я возвращать значение с выходом для каждой итерации в понимании списка
Ответ №2:
Прочитайте циклы for так, как если бы они были вложенными, слева направо. Выражение слева — это то, которое создает каждое значение в конечном списке:
for sublist in list_of_lists:
for item in sublist:
item # added to the list
Понимание списка также поддерживает if
тесты для фильтрации используемых элементов; их также можно рассматривать как вложенные инструкции, таким же образом, как for
циклы.
Добавив круглые скобки, вы изменили выражение; все, что в круглых скобках, теперь является добавляемым выражением левой стороны:
for item in sublist:
(item for sublist in list_of_lists) # added to the list
for
Подобный цикл является выражением генератора. Это работает точно так же, как понимание списка, за исключением того, что он не создает список. Вместо этого элементы создаются по требованию. Вы можете запросить выражение генератора для следующего значения, затем следующее значение и т.д.
В этом случае должен существовать ранее существующий sublist
объект, чтобы это вообще работало; в конце концов, внешний цикл list_of_lists
больше не завершен.
Ваша последняя попытка приводит к:
for sublist in (list_of_lists for item in sublist):
item # added to the list
Здесь list_of_lists
представлен элемент цикла в выражении генератора, выполняющий цикл поверх for item in sublist
. Опять же, sublist
должно уже существовать, чтобы это работало. Затем цикл добавляет уже существующий item
к конечному выводу списка.
В вашем случае, по-видимому, sublist
это список с 3 элементами в нем; ваш окончательный список произвел 3 элемента. item
было связано с 5
, поэтому вы получили 3 раза 5
в своем выводе.
Ответ №3:
Понимание списка работает следующим образом:
[<what i want> <for loops in the order you'd write them naturally>]
В этом случае <what I want>
это every item
в every sublist
. Чтобы получить эти элементы, вы просто перебираете подсписки в исходном списке и сохраняете / выводите каждый элемент в подсписке. Таким образом, порядок циклов for в понимании списка является тем же порядком, который вы бы использовали, если бы вы не использовали понимание списка. Единственная запутанная часть заключается в том, что <what I want>
идет первым, а не внутри тела последнего цикла.
Комментарии:
1. Вероятно, наиболее человеческое объяснение вложенных представлений.. Спасибо @timgeb