2012-04-27 45 views
7

Tôi cần phải lọc một danh sách lớn nhiều lần, nhưng tôi quan tâm đến cả sự đơn giản của mã và hiệu quả thực thi. Để đưa ra ví dụ:Tối ưu hóa danh sách lọc bằng 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()) 

Tôi lo ngại rằng tôi đang lặp qua danh sách quần áo khi thực tế nó đã được lặp lại. Tôi cũng muốn giữ hai hoạt động lọc riêng biệt, vì chúng thuộc về hai lớp khác nhau và tôi không muốn sao chép hàm lambda đầu tiên trong lớp mũ.

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

Tôi đã nghiên cứu các chức năng của trình tạo, vì chúng có vẻ như là cách để đi, nhưng tôi vẫn chưa tìm ra cách.

+0

Nếu bạn lo lắng về hiệu suất, bạn có ** thử nghiệm ** hiệu suất không? –

+0

Tôi sẽ có nếu tôi nghĩ rằng nó không rõ ràng. – cammil

+2

"hiển nhiên" là một từ nguy hiểm khi nói đến hiệu suất. –

Trả lời

23

Trước hết, sử dụng kết hợp filter/lambda sẽ không còn được dùng nữa. Kiểu lập trình chức năng hiện tại được mô tả trong Python Functional Programming HOWTO.

Thứ hai, nếu bạn quan tâm đến hiệu quả, thay vì xây dựng danh sách, bạn nên trả lại generators. Trong trường hợp này, chúng đủ đơn giản để sử dụng 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) 

Hoặc nếu bạn muốn, máy phát điện đúng (bị cáo buộc nhiều 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 

Nếu vì một số lý do, đôi khi bạn cần list hơn iterator, bạn có thể xây dựng danh sách bằng cách đúc đơn giản:

hats_list = list(get_hats()) 

Note, mà ở trên sẽ không xây dựng danh sách quần áo, do đó hiệu quả là gần duplicat của bạn phiên bản mã e.

+0

Hư hỏng, vartec. Lấy upvotes của tôi. –

+0

@ Li-aungYip: xin lỗi dude ;-) – vartec

+6

1) Kết hợp '' filter/lambda'' không được dùng nữa. 2) PEP 8 khuyên chống lại việc trả về biểu thức máy phát - chúng phải được tiêu thụ trong cùng phạm vi cho dù chúng được tạo ra - sử dụng một máy phát điện thông thường thay thế. 3). Nếu một danh sách là những gì cần thiết, OP nên sử dụng một danh sách hiểu chứ không phải là * danh sách * quấn quanh một genexp. –

4

Để làm điều đó trong một đường chuyền chỉ (giả):

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

Để làm điều đó trong một đường chuyền lớn và một đường chuyền nhỏ hơn (danh sách comprehensions):

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

Nếu bạn muốn tạo toàn bộ danh sách không có điểm nào bằng cách sử dụng biểu thức máy phát điện để đánh giá lười biếng, bởi vì bạn sẽ không lười biếng.

Nếu bạn chỉ muốn xử lý một vài thứ cùng một lúc hoặc nếu bạn bị hạn chế về bộ nhớ, hãy sử dụng giải pháp máy phát điện @ vartec.

+1

bạn có thể muốn sửa chữa 'điều trong sử dụng things' – okm

+0

@okm: Không nhìn thấy nó, xin lỗi - bạn có thể xây dựng? –

+0

Tôi có nghĩa là '[điều trong quần áo nếu điều là mũ]' không phải là cú pháp chính xác, phải không? – okm

3

Tôi đang tìm kiếm danh sách lọc tương tự nhưng muốn có định dạng hơi khác so với những gì được trình bày ở đây.

Cuộc gọi get_hats() ở trên là tốt nhưng bị giới hạn trong việc tái sử dụng. Tôi đang tìm kiếm thứ gì đó giống như get_hats(get_clothes(all_things)), nơi bạn có thể chỉ định nguồn (all_things) và sau đó là ít hoặc nhiều mức bộ lọc get_hats(), get_clothes() tùy thích.

Tôi tìm thấy một cách để làm điều đó với máy phát điện:

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 

này sau đó có thể được gọi bởi:

get_hats(get_clothes(all_things)) 

Tôi đã thử nghiệm các giải pháp ban đầu, giải pháp vartec và giải pháp bổ sung này để xem hiệu quả, và hơi ngạc nhiên bởi kết quả. Mã như sau:

Setup:

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 

giải pháp gốc:

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) 

Giải pháp của tôi:

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 của giải pháp:

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 
đang

Thời gian:

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) 

Kết quả:

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 

Các phát biểu dường như nhanh hơn một chút, sự khác biệt về thời gian giữa các giải pháp và vartec của tôi có lẽ chỉ tiếng ồn. Nhưng tôi thích sự linh hoạt của việc có thể áp dụng bất kỳ bộ lọc nào được yêu cầu theo thứ tự nào.

Các vấn đề liên quan