Используйте случай для вложенных/множественных списков или выражений генератора. Когда он более изящный?

Я иногда вижу подобное:

(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]