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

StackOverflow https://stackoverflow.com/questions/648675

Вопрос

Иногда я вижу подобные вещи:

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

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

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

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

Это было полезно?

Решение

Проверить БОДРОСТЬ ДУХА 202 именно здесь в язык был введен синтаксис понимания списков.

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

  • Форма [...для x...для y ...] гнезд, причем последний индекс изменяется быстрее всего, точно так же, как вложенные циклы for.

Также из 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, поскольку for циклы легко анализируются визуально.

Другие советы

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

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

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

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)

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

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

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

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

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

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

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

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

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

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

Выражение:

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

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

(i for i in xrange(10))

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

xrange(10)

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

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

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

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

output = [item += 1 for item in list if item >= 1]
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top