Можно ли вернуть 2 (или более) элемента для каждого элемента в понимании списка?
Что хочу (пример):
[f(x), g(x) for x in range(n)]
должен вернуться [f(0), g(0), f(1), g(1), ..., f(n-1), g(n-1)]
Итак, что-то для замены этого блока кода:
result = list()
for x in range(n):
result.add(f(x))
result.add(g(x))
python
list-comprehension
Хашмуш
источник
источник
Ответы:
>>> from itertools import chain >>> f = lambda x: x + 2 >>> g = lambda x: x ** 2 >>> list(chain.from_iterable((f(x), g(x)) for x in range(3))) [2, 0, 3, 1, 4, 4]
Сроки:
from timeit import timeit f = lambda x: x + 2 g = lambda x: x ** 2 def fg(x): yield f(x) yield g(x) print timeit(stmt='list(chain.from_iterable((f(x), g(x)) for x in range(3)))', setup='gc.enable(); from itertools import chain; f = lambda x: x + 2; g = lambda x: x ** 2') print timeit(stmt='list(chain.from_iterable(fg(x) for x in range(3)))', setup='gc.enable(); from itertools import chain; from __main__ import fg; f = lambda x: x + 2; g = lambda x: x ** 2') print timeit(stmt='[func(x) for x in range(3) for func in (f, g)]', setup='gc.enable(); f = lambda x: x + 2; g = lambda x: x ** 2') print timeit(stmt='list(chain.from_iterable((f(x), g(x)) for x in xrange(10**6)))', setup='gc.enable(); from itertools import chain; f = lambda x: x + 2; g = lambda x: x ** 2', number=20) print timeit(stmt='list(chain.from_iterable(fg(x) for x in xrange(10**6)))', setup='gc.enable(); from itertools import chain; from __main__ import fg; f = lambda x: x + 2; g = lambda x: x ** 2', number=20) print timeit(stmt='[func(x) for x in xrange(10**6) for func in (f, g)]', setup='gc.enable(); f = lambda x: x + 2; g = lambda x: x ** 2', number=20)
источник
(f(x), g(x))
. Может быть лучше записать в виде:def fg(x): yield x + 2; yield x ** 2; list(chain.from_iterable(fg(x) for x in range(3)))
.chain.from_iterable((func(x) for func in funcs) for x in range(n)))
. Что, кстати, сняло бы жалобу хачика. (Хотя в некотором смысле, мой и его, по сути, одинаковы с точки зрения процесса. Мы просто по-разному определяем внутренний генератор.)sum(..., [])
ответ, потому что не требуется воссоздавать список на каждом + (таким образом, имеет производительность O (N), а не производительность O (N ^ 2)). Я все равно буду использовать,sum(..., [])
когда мне нужен быстрый однострочный текст, или я тороплюсь, или когда количество объединяемых терминов ограничено (например, <= 10).[y for x in range(n) for y in (f(x), g(x))]
но, вероятно, это медленнее. @jamylak Вы тоже можете это проверить, если хотите.Понимание двойного списка:
[f(x) for x in range(5) for f in (f1,f2)]
Демо:
>>> f1 = lambda x: x >>> f2 = lambda x: 10*x >>> [f(x) for x in range(5) for f in (f1,f2)] [0, 0, 1, 10, 2, 20, 3, 30, 4, 40]
источник
for x in range(5): for f in (f1, f2): newlist.append(f(x))
. Раньше я находил их немного запутанными, потому что я все время пытался изменить порядок.sum( ([f(x),g(x)] for x in range(n)), [] )
Это эквивалентно
[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
Вы также можете думать об этом как о:
def flatten(list): ... flatten( [f(x),g(x)] for x in ... )
примечание: правильный способ - использовать
itertools.chain.from_iterable
или понимание двойного списка. (Это не требует повторного создания списка на каждом +, поэтому производительность O (N), а не O (N ^ 2).) Я все равно буду использовать,sum(..., [])
когда мне нужен быстрый однострочный текст или я тороплюсь. , или когда количество объединяемых терминов ограничено (например, <= 10). Вот почему я до сих пор упоминаю об этом здесь с этой оговоркой. Вы также можете использовать кортежи:((f(x),g(x)) for ...), ()
(или для каждого комментария хачика, имея генератор fg (x), который дает двухкортеж).источник
[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
sum()
такое злоупотребление антипаттерном и не вижу никакого оправдания его использованию при любых обстоятельствах. Код в вашем другом ответе меньше набирает, поэтому даже оправдание «когда мне нужен быстрый однострочный текст или я тороплюсь» на самом деле не помогает.Эта лямбда-функция объединяет два списка в один:
zipped = lambda L1, L2: [L[i] for i in range(min(len(L1), len(L2))) for L in (L1, L2)]
Пример:
>>> f = [x for x in range(5)] >>> g = [x*10 for x in range(5)] >>> zipped(f, g) [0, 0, 1, 10, 2, 20, 3, 30, 4, 40]
источник
Я знаю, что OP ищет решение для понимания списка, но я хотел бы предложить альтернативное использование
list.extend()
.f = lambda x: x g = lambda x: 10*x result = [] extend = result.extend for x in range(5): extend((f(x),g(x)))
что немного быстрее, чем использование двойного списка.
nums = range(100000) def double_comprehension(): return [func(x) for x in nums for func in (f,g)] def list_extend(): result = [] extend = result.extend for x in nums: extend((f(x),g(x))) return result %timeit -n100 double_comprehension() 23.4 ms ± 67 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) %timeit -n100 list_extend() 20.5 ms ± 213 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Версия Python: 3.8.0
источник
Решение с использованием reduce :
from functools import reduce f = lambda x: f"f({x})" ## Just for example g = lambda x: f"g({x})" data = [1, 2, 3] reduce(lambda acc, x: acc + [f(x), g(x)], data, []) # => ['f(1)', 'g(1)', 'f(2)', 'g(2)', 'f(3)', 'g(3)']
Хотя это и не понимание списка, это функциональный способ решения проблемы. Понимание списка - это, по сути, еще один способ обработки
map
данных, но в этом случае, когда отображение не однозначно между входом и выходом,reduce
остается некоторое пространство для маневра с тем, как может быть сгенерирован выход.В общем, любая
for
реализация формы:result = [] for n in some_data: result += some_operation() ## etc.
(Т.е. для циклов, предназначенных для создания побочного эффекта для списка или аналогичной структуры данных)
Может быть преобразован в декларативную
map/reduce/filter
реализацию.источник