Вариант использования для вложенных / нескольких списков или генерации выражений. Когда это более элегантно? - PullRequest
12 голосов
/ 16 марта 2009

Иногда я вижу такие вещи:

(k for k in (j for j in (i for i in xrange(10))))

Теперь это действительно изгибает мой мозг, и я бы предпочел, чтобы это не было представлено таким образом.

Существуют ли какие-либо варианты использования или примеры использования этих вложенных выражений, когда они были более элегантными и удобочитаемыми, чем если бы это был вложенный цикл?

Редактировать: Спасибо за примеры способов упростить это. Это на самом деле не то, о чем я просил, мне было интересно, были ли случаи, когда это было элегантно.

Ответы [ 7 ]

19 голосов
/ 16 марта 2009

Проверка PEP 202 , где был введен синтаксис списочных представлений для языка.

Для понимания вашего примера есть простое правило от самого Гвидо:

  • Форма [... для 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.

Если бы у вас была такая ситуация, вы могли бы найти ее более элегантной. ИМХО, тем не менее, несколько вложенных списков могут быть менее понятными в вашем коде, чем вложенные для циклов, поскольку циклы for легко анализируются визуально.

14 голосов
/ 16 марта 2009

Если вас беспокоит слишком большая сложность в одной строке, вы можете разделить ее:

(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 глубины, и в тот момент это было все еще довольно легко понять.

5 голосов
/ 16 марта 2009

В случае с вашим примером я, вероятно, написал бы это как:

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 голосов
/ 16 марта 2009

Поскольку они являются выражениями генератора, вы можете привязать каждое из них к собственному имени, чтобы сделать его более читабельным без каких-либо изменений в производительности. Изменение его на вложенный цикл, скорее всего, отрицательно скажется на производительности.

irange = (i for i in xrange(10))
jrange = (j for j in irange)
krange = (k for k in jrange)

Неважно, какой вы выберете, я думаю, что многострочный пример в целом более читабелен.

1 голос
/ 25 января 2016

Предостережение: элегантность отчасти зависит от вкуса.

Понимания списка никогда не бывают более чистыми , чем соответствующий расширенный цикл for. Циклы for также более мощнее , чем списки. Так зачем вообще их использовать?

Что такое списочное понимание: сжатое - они позволяют вам что-то делать в одной строке.

Время для использования понимания списка - когда вам нужен определенный список, он может быть создан на лету довольно легко, и вам не нужны или не нужны промежуточные объекты, висящие вокруг. Это может произойти, когда вам нужно упаковать некоторые объекты в текущей области в один объект, который вы можете передать в функцию, как показано ниже:

list1 = ['foo', 'bar']
list2 = ['-ness', '-ity']
return filterRealWords([str1+str2 for str1 in list1 for str2 in list2])

Этот код примерно так же удобен для чтения, как расширенная версия, но он намного короче. Это позволяет избежать создания / именования объекта, который используется только один раз в текущей области видимости, что, возможно, более элегантно.

0 голосов
/ 27 марта 2014

Я считаю, что это может быть полезно и элегантно в таких ситуациях, когда у вас есть такой код:

output = []
for item in list:
    if item >= 1:
        new = item += 1
        output.append(new)

Вы можете сделать его однострочным, как это:

output = [item += 1 for item in list if item >= 1]
0 голосов
/ 16 марта 2009

Выражение:

(k for k in (j for j in (i for i in xrange(10))))

эквивалентно:

(i for i in xrange(10))

это почти то же самое:

xrange(10)

Последний вариант более элегантен, чем первый.

...