リスト内包表記:各アイテムに対して2つ(またはそれ以上)のアイテムを返す


88

リスト内包の項目ごとに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))

2
好奇心から、なぜこれをしたいのですか?このようにせずに最終目標を達成するより良い方法があるかもしれません。
murgatroid99 2012

3
主に関数型プログラミングが好きだからです。座標のリストを画面座標のタプルにマップして、pyglet.graphics.draw関数で使用します。
Hashmush 2012

回答:


52
>>> 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


4
このコードは不要なタプルを作成します(f(x), g(x))。次のように書くのがよいでしょうdef fg(x): yield x + 2; yield x ** 2; list(chain.from_iterable(fg(x) for x in range(3)))
khachik

1
で一般化することもできchain.from_iterable((func(x) for func in funcs) for x in range(n)))ます。これは偶然にもハチクの不満を解消するでしょう。(ある意味では、私のものと彼はプロセスの点で本質的に同じです。単に内部ジェネレータを異なるように定義します。)
JAB

sum(..., [])+ごとにリストを再作成する必要がないため、これは私の回答よりも優れています(したがって、O(N ^ 2)パフォーマンスではなくO(N)パフォーマンスが得られます)。sum(..., [])迅速なワンライナーが必要な場合、または急いでいる場合、または結合される用語の数が制限されている場合(たとえば、<= 10)でも、引き続き使用します。
ninjagecko

@khachikこれはもっと速いだろうと思いますが、両方のメソッドの時間を計測しますが、タプルはPythonで非常に速く生成されます。
jamylak

3
消えた3番目の答えは次のように [y for x in range(n) for y in (f(x), g(x))]なります。しかし、これはおそらく遅いです。@jamylak必要に応じて、これもテストできます。
Hashmush 2012

114

二重リスト内包:

[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]

10
これは、二重リストのカンプがそれほど怖くないことを示しているので、すばらしいです。それらは、forループのように書かれたforループを単にネストしただけです。 for x in range(5): for f in (f1, f2): newlist.append(f(x))。私は順序を逆転させようとし続けたので、それらを少し混乱させていました。
DSM

1
これは受け入れられる答えになるはずです、ありがとう、すごい!
Wingjam 2017

@DSM私は思う、それは永遠に混乱するでしょう。)
Winand

11
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 ^ 2)のパフォーマンスではなくO(N)のパフォーマンスが得られます。)sum(..., [])迅速なワンライナーが必要な場合や急いでいる場合でも、引き続き使用します。、または結合される用語の数が制限されている場合(例:10以下)。そのため、ここではまだこの警告を付けて言及しています。タプルを使用することもできます:(((f(x),g(x)) for ...), ()またはkhachikのコメントに従って、2つのタプルを生成するジェネレーターfg(x)があります)。


@ArashThr:それはやっている[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
ninjagecko 2012

正確に何をしているのか説明できますか?
Rsh

注:これにはO(N ^ 2)ランタイムがあるため、巨大なリストでは遅くなる可能性があります。
jamylak

1
@jamylak:はい、私もコメントであなたの答えでこれを述べました。=)
ninjagecko

sum()はこのようにアンチパターンを乱用することを検討していますが、どのような状況でもそれを使用する正当な理由はありません。あなたの他の答えのコードはタイピングが少ないので、「簡単なワンライナーが欲しいとき、または私が急いでいるとき」という言い訳でさえ、実際にはそれをカットしません。
Sven Marnach、2012

2

このラムダ関数は、2つのリストを1つのリストに圧縮します。

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]

1

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.