¿Es posible devolver 2 (o más) elementos para cada elemento en una lista de comprensión?
Lo que quiero (ejemplo):
[f(x), g(x) for x in range(n)]
debería volver [f(0), g(0), f(1), g(1), ..., f(n-1), g(n-1)]
Entonces, algo para reemplazar este bloque de código:
result = list()
for x in range(n):
result.add(f(x))
result.add(g(x))
python
list-comprehension
Hachís
fuente
fuente
Respuestas:
>>> 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]
Tiempos:
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)
fuente
(f(x), g(x))
. Podría ser mejor escrito como: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)))
. Lo que, por cierto, eliminaría la queja de Khachik. (Aunque en cierto sentido, el mío y el suyo son esencialmente lo mismo en términos de proceso. Simplemente definimos el generador interno de manera diferente.)sum(..., [])
respuesta porque no requiere recrear la lista en cada + (por lo tanto, tiene un rendimiento O (N) en lugar de un rendimiento O (N ^ 2)). Lo seguiré usandosum(..., [])
cuando quiera un resumen rápido o tenga prisa, o cuando el número de términos que se combinan sea limitado (por ejemplo, <= 10).[y for x in range(n) for y in (f(x), g(x))]
pero probablemente esto sea más lento. @jamylak También puedes probar esto si quieres.Comprensión de doble lista:
[f(x) for x in range(5) for f in (f1,f2)]
Manifestación:
>>> 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]
fuente
for x in range(5): for f in (f1, f2): newlist.append(f(x))
. Solía encontrarlos un poco confusos porque seguía intentando invertir el orden.sum( ([f(x),g(x)] for x in range(n)), [] )
Esto es equivalente a
[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
También puede pensar en ello como:
def flatten(list): ... flatten( [f(x),g(x)] for x in ... )
nota: La forma correcta es utilizar
itertools.chain.from_iterable
o la comprensión de doble lista. (No requiere recrear la lista en cada +, por lo tanto, tiene un rendimiento O (N) en lugar de un rendimiento O (N ^ 2)). Lo seguiré usandosum(..., [])
cuando quiera una frase rápida o tenga prisa. , o cuando el número de términos que se combinan está acotado (por ejemplo, <= 10). Por eso todavía lo menciono aquí, con esta salvedad. También puede usar tuplas:((f(x),g(x)) for ...), ()
(o según el comentario de Khachik, que tiene un generador fg (x) que produce una tupla de dos).fuente
[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
sum()
de esta manera como un antipatrón, y no veo ninguna justificación para usarlo en cualquier circunstancia. El código en su otra respuesta es menos tipeado, por lo que incluso la excusa "cuando quiero una frase rápida o tengo prisa" no es suficiente.Esta función lambda comprime dos listas en una sola:
zipped = lambda L1, L2: [L[i] for i in range(min(len(L1), len(L2))) for L in (L1, L2)]
Ejemplo:
>>> 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]
fuente
Sé que OP está buscando una solución de comprensión de listas, pero me gustaría ofrecer una alternativa usando
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)))
que es ligeramente más rápido que usar la comprensión de lista doble.
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)
Versión de Python: 3.8.0
fuente
Una solución que usa reducir :
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)']
Si bien no es una lista de comprensión, esta es una forma funcional de abordar el problema. La comprensión de una lista es esencialmente otra forma de
map
analizar los datos, pero en este caso, donde el mapeo no es uno a uno entre la entrada y la salida,reduce
permite cierto margen de maniobra con respecto a cómo se puede generar la salida.En general, cualquier
for
implementación del formulario:result = [] for n in some_data: result += some_operation() ## etc.
(Es decir, para bucles destinados a producir un efecto secundario en una lista o estructura de datos similar)
Se puede refactorizar en una
map/reduce/filter
implementación declarativa .fuente