在Python 2.7中优化过滤列表

cam*_*mil 9 python generator python-2.7

我需要多次过滤大型列表,但我关注代码的简单性和执行效率.举个例子:

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())
Run Code Online (Sandbox Code Playgroud)

我担心我正在迭代衣服清单,实际上它已经被迭代了.我还想将两个过滤器操作分开,因为它们属于两个不同的类,我不想复制hats类中的第一个lambda函数.

# 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)
Run Code Online (Sandbox Code Playgroud)

我一直在调查发电机功能,因为它们似乎是要走的路,但我还没有弄清楚如何.

var*_*tec 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)
Run Code Online (Sandbox Code Playgroud)

或者,如果你愿意,真正的发电机(据称更加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
Run Code Online (Sandbox Code Playgroud)

如果由于某种原因,有时你需要list而不是iterator,你可以通过简单的转换构建列表:

hats_list = list(get_hats())
Run Code Online (Sandbox Code Playgroud)

注意,上面不会构建衣服列表,因此效率接近您的重复代码版本.

  • 1)不推荐使用``filter/lambda``组合.2)PEP 8建议不要返回生成器表达式 - 无论是否创建它们都应该在相同的范围内使用 - 而是使用常规生成器.3).如果列表是需要的,那么OP应该使用列表推导而不是*list*包裹在genexp中. (6认同)

All*_*an5 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
Run Code Online (Sandbox Code Playgroud)

然后可以通过以下方式调用它:

get_hats(get_clothes(all_things))
Run Code Online (Sandbox Code Playgroud)

我测试了原始解决方案,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
Run Code Online (Sandbox Code Playgroud)

原始解决方案

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)
Run Code Online (Sandbox Code Playgroud)

我的解决方案

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

时间码:

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)
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

生成器表达式似乎稍快,我和vartec解决方案之间的时间差异可能只是噪音.但我更喜欢能够以任何顺序应用任何过滤器的灵活性.