Вопрос по python, list-comprehension – Python (понимание списка): возвращение двух (или более) элементов для каждого элемента

71

Is it possible to return 2 (or more) items for each item in a list comprehension?

Что я хочу (пример):

[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))
Главным образом потому, что я люблю функциональное программирование. Я хочу отобразить список координат на корте экранных координат, чтобы использовать их с функцией pyglet.graphics.draw. Hashmush
Из любопытства, почему вы хотите это сделать? Там может быть лучший способ достижения вашей конечной цели, не пытаясь сделать это таким образом. murgatroid99

Ваш Ответ

4   ответа
77

Понимание двойного списка:

[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]
Это хорошо, потому что показывает, что двойные списки не так страшны: они просто вложены в записанные циклыjust like for loops. for x in range(5): for f in (f1, f2): newlist.append(f(x)), Я находил их немного запутанными, потому что я продолжал пытаться изменить порядок.
Это должен быть принятый ответ, спасибо, удивительно!
@ninjagecko: опечатка, (f1, f1).
2

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]
9
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(..., []) когда мне нужна быстрая однострочность или я в спешке, или когда число объединяемых терминов ограничено (например, & lt; = 10). Вот почему я все еще упоминаю это здесь, с этим предостережением. Вы также можете использовать кортежи:((f(x),g(x)) for ...), () (или согласно комментарию Хачика, имеющему генератор fg (x), который выдает двойную кортеж).

@ArashThr: это делает[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
Хороший трюк. Это гораздо более приемлемо.
Примечание. Это время выполнения O (N ^ 2), поэтому оно может быть медленным в огромных списках.
Можете ли вы объяснить, что именно он делает?
44
>>> 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)

2.69210777094

3.13900787874

1.62461071932

25.5944058287

29.2623711793

25.7211849286

Вы могли бы даже обобщить это сchain.from_iterable((func(x) for func in funcs) for x in range(n))), Что, кстати, исключило бы жалобу Хачика. (Хотя в некотором смысле мой и его по сути одинаковы с точки зрения процесса. Мы просто определяем внутренний генератор по-разному.)
Третий ответ, который исчез, выглядел так:[y for x in range(n) for y in (f(x), g(x))] Но это, вероятно, медленнее. @jamylak Вы можете проверить это тоже, если хотите. Hashmush
Это лучше чем мойsum(..., []) ответ, потому что он не требует воссоздания списка на каждом + (таким образом, имеет производительность O (N), а не производительность O (N ^ 2)). Я все еще буду использоватьsum(..., []) когда мне нужна быстрая однострочность или я в спешке, или когда число объединяемых терминов ограничено (например, & lt; = 10).
@khachik Я думаю, это будет быстрее, но сейчас я опишу оба метода, хотя в python кортежи генерируются очень быстро.
Этот код создает ненужные кортежи(f(x), g(x)), Может быть лучше написано как:def fg(x): yield x + 2; yield x ** 2; list(chain.from_iterable(fg(x) for x in range(3))).

Похожие вопросы