Используйте случай для вложенных/множественных списков или выражений генератора. Когда он более изящный?
Я иногда вижу подобное:
(k for k in (j for j in (i for i in xrange(10))))
Теперь это действительно изгибает мой мозг, и я предпочел бы, чтобы он не был представлен таким образом.
Существуют ли какие-либо варианты использования или примеры использования этих вложенных выражений, где он был более изящным и читаемым, чем если бы он был вложенным циклом?
Изменить: Спасибо за примеры способов упрощения этого. На самом деле это не то, о чем я просил, мне было интересно, были ли времена, когда это было изящно.
Ответы
Ответ 1
Отметьте PEP 202, в котором был введен синтаксис понятий в виде списка.
Для понимания вашего примера из самого Guido существует простое правило:
- Форма [... для x... для y...] гнездится с последним индексом изменяясь быстрее, точно так же, как вложенные для циклов.
Также из PEP 202, который отвечает на ваш вопрос:
Rationale
List comprehensions provide a more concise way to create lists in
situations where map() and filter() and/or nested loops would
currently be used.
Если бы у вас была такая ситуация, вы могли бы найти ее более элегантной. IMHO, однако, множественные вложенные проверки списка могут быть менее ясными в вашем коде, чем вложенные для циклов, так как циклы for
легко анализируются визуально.
Ответ 2
Если вы беспокоитесь о слишком большой сложности в одной строке, вы можете разбить ее:
(k for k in
(j for j in
(i for i in xrange(10))))
Я всегда находил продолжение строк, чтобы выглядеть немного странным в Python, но это облегчает просмотр того, что происходит в каждом из них. Поскольку дополнительное задание/поиск не собирается ничего делать или что-либо сломать, вы также можете написать его следующим образом:
gen1 = (i for i in xrange(10))
gen2 = (j for j in gen1)
gen3 = (k for k in gen2)
На практике я не думаю, что я когда-либо вставлял понимание более чем на 2 глубины, и в этот момент все еще было довольно легко понять.
Ответ 3
В случае вашего примера я, вероятно, напишу его как:
foos = (i for i in xrange(10))
bars = (j for j in foos)
bazs = (k for k in bars)
Учитывая более описательные имена, я думаю, что это, вероятно, будет вполне ясным, и я не могу себе представить, что есть какая-то измеримая разница в производительности.
Возможно, вы больше думаете о выражениях:
(x for x in xs for xs in ys for ys in lst)
- на самом деле, это даже не актуально. Вы должны положить вещи в другом порядке:
(x for ys in lst for xs in ys for x in xs)
Я мог бы написать это как быстрый способ сглаживания списка, но в целом я думаю, что вы пишете: время, которое вы сохраняете, набрав меньше, обычно уравновешивается дополнительным временем, которое вы тратите на правильное выражение генератора.
Ответ 4
Поскольку они являются генераторными выражениями, вы можете привязать их к собственному имени, чтобы сделать его более читаемым без каких-либо изменений в производительности. Изменение его на вложенный цикл, вероятно, будет пагубным для производительности.
irange = (i for i in xrange(10))
jrange = (j for j in irange)
krange = (k for k in jrange)
Неважно, что вы выберете, я думаю, что пример с несколькими строками более читабельен вообще.
Ответ 5
Предостережение: элегантность отчасти зависит от вкуса.
Сопоставление списков никогда не бывает более ясным, чем соответствующий расширенный цикл. Для петель также более мощные, чем списки. Так зачем вообще их использовать?
В каких списках понимается краткий - они позволяют вам что-то делать в одной строке.
Время использования понимания списка - это когда вам нужен определенный список, его можно легко создать на лету, и вам не нужны или нужны промежуточные объекты. Это может произойти, если вам нужно упаковать некоторые объекты в текущую область действия в один объект, который вы можете использовать в функции, например ниже:
list1 = ['foo', 'bar']
list2 = ['-ness', '-ity']
return filterRealWords([str1+str2 for str1 in list1 for str2 in list2])
Этот код примерно такой же читабельный, как и расширенная версия, но он намного короче. Он избегает создания/именования объекта, который используется только один раз в текущей области, что, возможно, более элегантно.
Ответ 6
Выражение:
(k for k in (j for j in (i for i in xrange(10))))
эквивалентно:
(i for i in xrange(10))
что почти то же самое:
xrange(10)
Последний вариант более элегантный, чем первый.
Ответ 7
Я считаю, что это может быть полезно и элегантно в таких ситуациях, когда у вас есть такой код:
output = []
for item in list:
if item >= 1:
new = item += 1
output.append(new)
Вы можете сделать его одним слоем следующим образом:
output = [item += 1 for item in list if item >= 1]