9

大きなリストを何度もフィルタリングする必要がありますが、コードの単純さと実行効率の両方に関心があります。例を挙げると:

all_things # huge collection of all things

# inefficient but clean code
def get_clothes():
    return filter(lambda t: t.garment, allThings)

def get_hats():
    return filter(lambda t: t.headgear, get_clothes())

実際にはすでに反復されているのに、服のリストを反復しているのではないかと心配しています。また、2 つのフィルター操作は 2 つの異なるクラスに属しているため、別々に保持したいと考えており、hats クラスの最初のラムダ関数を複製したくありません。

# efficient but duplication of code
def get_clothes():
    return filter(lambda t: t.garment, allThings)

def get_hats():
    return filter(lambda t: t.headgear and t.garment, allThings)

ジェネレーター関数が進むべき道のように思えたので、私はジェネレーター関数を調査してきましたが、まだその方法を理解していません。

4

3 に答える 3

25

まず、filter/のlambda組み合わせの使用は非推奨になります。現在の関数型プログラミングスタイルは、Python関数型プログラミングHOWTOで説明されています。

次に、リストを作成するのではなく、効率に関心がある場合は、ジェネレーターを返す必要があります。この場合、それらはジェネレータ式を使用するのに十分単純です。

def get_clothes():
    return (t for t in allThings if t.garment)

def get_hats():
    return (t for t in get_clothes() if t.headgear)

または、必要に応じて、真のジェネレーター(よりpythonicと言われています):

def get_clothes():
    for t in allThings:
       if t.garment:
           yield t

def get_hats():
    for t in get_clothes():
        if t.headgear:
            yield t

何らかの理由で、listではなく必要な場合はiterator、単純なキャストでリストを作成できます。

hats_list = list(get_hats())

上記は服のリストを作成しないため、効率は重複コードバージョンに近いことに注意してください。

于 2012-04-27T11:37:40.157 に答える
5

私はリストの同様のフィルタリングを探していましたが、ここに示されているものとは少し異なる形式にしたかったのです。

上記のget_hats()呼び出しは適切ですが、再利用には制限があります。get_hats(get_clothes(all_things))私は、ソースを指定してから、必要に応じてフィルターのレベルをいくつでも指定できる、のよう(all_things)なものを探していました。get_hats()get_clothes()

私はジェネレーターでそれを行う方法を見つけました:

def get_clothes(in_list):
    for item in in_list:
        if item.garment:
            yield item

def get_hats(in_list):
    for item in in_list:
        if item.headgear:
            yield item

これは、次の方法で呼び出すことができます。

get_hats(get_clothes(all_things))

元のソリューション、vartecのソリューション、およびこの追加のソリューションをテストして効率を確認しましたが、その結果には多少驚いていました。次のようにコーディングします。

設定:

class Thing:
    def __init__(self):
        self.garment = False
        self.headgear = False

all_things = [Thing() for i in range(1000000)]

for i, thing in enumerate(all_things):
    if i % 2 == 0:
        thing.garment = True
    if i % 4 == 0:
        thing.headgear = True

元の解決策:

def get_clothes():
    return filter(lambda t: t.garment, all_things)

def get_hats():
    return filter(lambda t: t.headgear, get_clothes())

def get_clothes2():
    return filter(lambda t: t.garment, all_things)

def get_hats2():
    return filter(lambda t: t.headgear and t.garment, all_things)

私の解決策:

def get_clothes3(in_list):
    for item in in_list:
        if item.garment:
            yield item

def get_hats3(in_list):
    for item in in_list:
        if item.headgear:
            yield item

vartecのソリューション:

def get_clothes4():
    for t in all_things:
       if t.garment:
           yield t

def get_hats4():
    for t in get_clothes4():
        if t.headgear:
            yield t

タイミングコード:

import timeit

print 'get_hats()'
print timeit.timeit('get_hats()', 'from __main__ import get_hats', number=1000)

print 'get_hats2()'
print timeit.timeit('get_hats2()', 'from __main__ import get_hats2', number=1000)

print '[x for x in get_hats3(get_clothes3(all_things))]'
print timeit.timeit('[x for x in get_hats3(get_clothes3(all_things))]',
                    'from __main__ import get_hats3, get_clothes3, all_things',
                    number=1000)

print '[x for x in get_hats4()]'
print timeit.timeit('[x for x in get_hats4()]',
                    'from __main__ import get_hats4', number=1000)

結果:

get_hats()
379.334653854
get_hats2()
232.768362999
[x for x in get_hats3(get_clothes3(all_things))]
214.376812935
[x for x in get_hats4()]
218.250688076

ジェネレーターの式はわずかに速いように見えます。私のソリューションとvartecのソリューションの時間差はおそらく単なるノイズです。しかし、私は、必要なフィルターを任意の順序で適用できる柔軟性を好みます。

于 2012-10-30T03:20:48.083 に答える
4

ワンパスでのみそれを行うには(擬似コード):

clothes = list()
hats = list()
for thing in things:
    if thing is a garment:
        clothes.append(thing)
        if thing is a hat:
            hats.append(thing)

1つの大きなパスと1つの小さなパス(リスト内包)でそれを行うには:

clothes = [ x for x in things if x is garment ]
hats = [ x for x in clothes if x is hat ]

リスト全体を作成したい場合は、遅延が発生しないため、遅延評価にジェネレータ式を使用しても意味がありません。

一度にいくつかのことだけを処理したい場合、またはメモリに制約がある場合は、@vartecのジェネレータソリューションを使用してください。

于 2012-04-27T11:37:31.107 に答える