Пример использования для понимания вложенных / множественных списков или выражений генератора.Когда это будет более элегантно?
-
19-08-2019 - |
Вопрос
Иногда я вижу подобные вещи:
(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]