Czy jest możliwe zwrócenie 2 (lub więcej) pozycji dla każdej pozycji w zrozumieniu listy?
Czego chcę (przykład):
[f(x), g(x) for x in range(n)]
powinien wrócić [f(0), g(0), f(1), g(1), ..., f(n-1), g(n-1)]
Więc coś, co zastąpi ten blok kodu:
result = list()
for x in range(n):
result.add(f(x))
result.add(g(x))
python
list-comprehension
Hashmush
źródło
źródło
Odpowiedzi:
>>> 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]
Czasy:
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)
źródło
(f(x), g(x))
. Mogłoby być lepiej zapisać jako: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)))
. Co, nawiasem mówiąc, wyeliminowałoby skargę chaczika. (Chociaż w pewnym sensie moje i jego są zasadniczo takie same pod względem procesu. Po prostu inaczej definiujemy wewnętrzny generator.)sum(..., [])
odpowiedź, ponieważ nie wymaga ponownego tworzenia listy na każdym + (w ten sposób ma wydajność O (N) zamiast wydajności O (N ^ 2)). Nadal będę używał,sum(..., [])
gdy chcę szybkiego, jednoliniowego tekstu, spieszy mi się lub gdy liczba łączonych terminów jest ograniczona (np. <= 10).[y for x in range(n) for y in (f(x), g(x))]
Ale to prawdopodobnie wolniej. @jamylak Możesz to też przetestować, jeśli chcesz.Rozumienie podwójnej listy:
[f(x) for x in range(5) for f in (f1,f2)]
Próbny:
>>> 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]
źródło
for x in range(5): for f in (f1, f2): newlist.append(f(x))
. Kiedyś uważałem je za trochę zagmatwane, ponieważ ciągle próbowałem odwrócić kolejność.sum( ([f(x),g(x)] for x in range(n)), [] )
Jest to równoważne z
[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
Możesz również myśleć o tym jako:
def flatten(list): ... flatten( [f(x),g(x)] for x in ... )
Uwaga: Właściwym sposobem jest użycie
itertools.chain.from_iterable
lub podwójne rozumienie listy. (To nie wymaga odtworzenia listy na każdy +, co ma O (n) wydajności zamiast O (n ^ 2) wydajność). Będę nadal korzystaćsum(..., [])
, gdy chcę szybko jedną wkładkę lub jestem w pośpiechu lub gdy liczba łączonych terminów jest ograniczona (np. <= 10). Dlatego wciąż o tym tutaj wspominam, z tym zastrzeżeniem. Możesz także użyć krotek:((f(x),g(x)) for ...), ()
(lub za komentarzem khaczika, mając generator fg (x), który daje dwie krotki).źródło
[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
sum()
w ten sposób jest antywzorem i nie widzę żadnego uzasadnienia, aby go używać w jakichkolwiek okolicznościach. Kod w Twojej drugiej odpowiedzi to mniej wpisywania, więc nawet wymówka „kiedy chcę szybkiej jednej linijki lub spieszę się” tak naprawdę nie wystarcza.Ta funkcja lambda zamyka dwie listy w jedną:
zipped = lambda L1, L2: [L[i] for i in range(min(len(L1), len(L2))) for L in (L1, L2)]
Przykład:
>>> 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]
źródło
Wiem, że OP szuka rozwiązania do rozumienia listy, ale chciałbym zaoferować alternatywne użycie
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)))
co jest nieznacznie szybsze niż używanie podwójnego rozumienia list.
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)
Wersja Pythona: 3.8.0
źródło
Rozwiązanie wykorzystujące redukuj :
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)']
Chociaż nie jest to rozumienie listy, jest to funkcjonalny sposób podejścia do problemu. Zrozumienie listy jest zasadniczo innym sposobem
map
na przeglądanie danych, ale w tym przypadku, gdy mapowanie nie jest jednym do jednego między wejściem a wyjściem,reduce
pozwala na pewne wahanie się w jaki sposób można wygenerować dane wyjściowe.Ogólnie rzecz biorąc, każda
for
implementacja formularza:result = [] for n in some_data: result += some_operation() ## etc.
(Tj. Pętle mające na celu wywołanie efektu ubocznego na liście lub podobnej strukturze danych)
Można zamienić na deklaratywną
map/reduce/filter
implementację.źródło