2012-04-27 4 views
7

큰 목록을 여러 번 필터링해야하지만 코드 단순성과 실행 효율성이 모두 중요합니다.파이썬 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()) 

내가 사실은 이미 이상 반복되었을 때 나는 옷 목록을 반복하고있어 것을 걱정 : 예를 제공합니다. 두 개의 다른 클래스에 속해 있기 때문에 두 개의 필터 연산을 별도로 유지하기를 원하며 모자 클래스의 첫 번째 람다 함수를 복제하고 싶지 않습니다.

# 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) 

나는 발전기 기능을 조사한 바 있습니다. 이동하는 것처럼 보였지만 아직 방법을 찾지 못했습니다.

+0

실적이 걱정된다면 ** 실적 **을 테스트 했습니까? –

+0

그것이 분명하지 않다고 생각한다면 나는 가질 것입니다. – cammil

+2

"명백한"은 성능면에서 위험한 단어입니다. –

답변

23

우선 filter/lambda 조합을 사용하는 것이 좋습니다. 현재 함수 프로그래밍 스타일은 Python Functional Programming HOWTO에 설명되어 있습니다.

둘째로, 목록을 구성하는 것이 아니라 효율성에 관심이 있다면 generators을 반환해야합니다. 이 경우 그들은 generator expressions을 사용할만큼 충분히 간단합니다.

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) 

아니면, 진정한 발전기 (추정 더 파이썬)를 원합니다 경우 :

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()) 

참고, 그 위의 것 옷하지 구조 목록, 따라서 효율성은 duplicat에 가까운 전자 코드 버전.

+0

저주, vartec. 내 upvotes 가져 가라. –

+0

@ Li-aungYip : 죄송합니다 .-) – vartec

+6

1)''filter/lambda'' 조합은 더 이상 사용되지 않습니다. 2) PEP 8은 발전기 표현을 반환하지 않도록 권고합니다. 생성 된 표현식은 생성 된 것처럼 동일한 범위에서 소비되어야하며 대신 일반 발전기를 사용합니다. 삼). 목록이 필요한 경우 OP는 genexp를 감싸는 * list * 대신 목록 이해력을 사용해야합니다. –

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) 

하나 개의 큰 패스와 하나 개의 작은 패스 (지능형리스트)에서 작업을 수행하려면 : 당신이 만들려면

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

전체 목록 게으르지 않을 것이므로 게으른 평가를 위해 생성자 표현식을 사용할 필요가 없습니다.

한 번에 몇 가지만 처리하거나 메모리가 부족한 경우 @ vartec의 생성기 솔루션을 사용하십시오.

+1

'물건에있는 것 '사용법을 고치고 싶을 수도 있습니다 – okm

+0

@okm : 보지 못해 죄송합니다 - 자세히 설명해 주시겠습니까? –

+0

나는 '물건이 모자라면 옷 속에있는 물건'을 의미하지 않습니다. 구문 상 정확하지 않습니까? – okm

3

비슷한 목록 필터링을 찾고 있었지만 여기에 제시된 것과 약간 다른 형식을 원했습니다.

위의 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의 솔루션 사이의 시간 차이는 아마 소음입니다. 그러나 나는 어떤 순서로든 필요한 모든 필터를 적용 할 수있는 유연성을 선호합니다.