2012-04-27 21 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) 

या आप चाहते हैं तो, सच जनरेटर (कथित तौर पर अधिक 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()) 

ध्यान दें, कि ऊपर होगा नहीं कपड़े का निर्माण सूची है, इस प्रकार दक्षता अपने duplicat के करीब है ई कोड संस्करण।

+0

दमन, वर्टेक। मेरे अपवॉट ले लो। –

+0

@ ली-औंगवाईप: क्षमा करें दोस्त ;-) – vartec

+6

1) '' फ़िल्टर/लैम्ब्डा' संयोजन को बहिष्कृत नहीं किया गया है। 2) पीईपी 8 जनरेटर अभिव्यक्ति को लौटने के खिलाफ सलाह देता है - उनको उसी क्षेत्र में खपत किया जाना चाहिए चाहे वे बनाए गए हों - इसके बजाय एक नियमित जनरेटर का उपयोग किया जाता है। 3)। यदि एक सूची की आवश्यकता है, तो ओपी को एक जीनएक्सपी के चारों ओर लिपटे * सूची * की बजाय सूची समझ का उपयोग करना चाहिए। –

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) 

एक बड़ा पास और एक छोटे पास (सूची comprehensions) में यह ऐसा करने के लिए:

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

आप बनाना चाहते हैं पूरी सूची आलसी मूल्यांकन के लिए जेनरेटर अभिव्यक्ति का उपयोग करने का कोई मतलब नहीं है, क्योंकि आप आलसी नहीं होने जा रहे हैं।

यदि आप एक समय में केवल कुछ चीजों से निपटना चाहते हैं, या यदि आप स्मृति-बाधित हैं, तो @ वर्टेक के जनरेटर समाधान का उपयोग करें।

+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 के समाधान के बीच के समय में अंतर शायद सिर्फ शोर कर रहे हैं। लेकिन मैं जो कुछ भी क्रम में आवश्यक है उसे लागू करने में सक्षम होने की लचीलापन पसंद करता हूं।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^