2008-08-20 12 views
50

जब मैं पायथन में कोड लिख रहा हूं, तो मुझे अक्सर कुछ मानदंडों के आधार पर किसी सूची या अन्य अनुक्रम प्रकार से वस्तुओं को हटाने की आवश्यकता होती है। मुझे एक ऐसा समाधान नहीं मिला है जो सुरुचिपूर्ण और कुशल है, क्योंकि वर्तमान में जिस सूची में आप इसे चालू कर रहे हैं, उससे वस्तुओं को हटा देना बुरा है। उदाहरण के लिए, यदि आप ऐसा नहीं कर सकते हैं:पायथन में अनुक्रम से वस्तुओं को हटाने के लिए सुरुचिपूर्ण तरीका?

toremove = [] 
for name in names: 
    if name[-5:] == 'Smith': 
     toremove.append(name) 
for name in toremove: 
    names.remove(name) 
del toremove 

यह innefficient, काफी बदसूरत और संभवतः गाड़ी (यह कैसे निपटता है कई 'जॉन है:

for name in names: 
    if name[-5:] == 'Smith': 
     names.remove(name) 

मैं आमतौर पर कुछ इस तरह कर रही अंत स्मिथ की प्रविष्टियां?)। क्या किसी के पास एक अधिक सुरुचिपूर्ण समाधान है, या कम से कम एक अधिक कुशल है?

शब्दकोशों के साथ काम करने वाले एक के बारे में कैसे?

+0

आपका कोड एकाधिक स्मिथ को हटा देता है या आपने इसे संपादित किया है? – systemovich

उत्तर

52

दो आसान तरीके का उपयोग करते हुए बस को छानने पूरा करने के लिए कर रहे हैं:

  1. filter का उपयोग करना:

    names = filter(lambda name: name[-5:] != "Smith", names)

  2. का उपयोग सूची comprehensions:

    names = [name for name in names if name[-5:] != "Smith"]

ध्यान दें कि दोनों ही मामलों मूल्यों जिसके लिए विधेय समारोह True का मूल्यांकन रखना है, तो आप (तर्क उल्टा करने के लिए है अर्थात आप कहते हैं, "उन लोगों को रखें जिनके पास अंतिम नाम स्मिथ नहीं है" के बजाय "जिन लोगों का अंतिम नाम स्मिथ है")।

संपादित करें मजेदार ... दो लोगों ने व्यक्तिगत रूप से दोनों उत्तरों को पोस्ट किया क्योंकि मैंने सुझाव दिया था कि मैं अपना पोस्ट कर रहा हूं।

+0

इसके अलावा, जनरेटर अभिव्यक्तियां। –

+12

'name.endswith (" स्मिथ ") 'बहुत अच्छा दिखता है :-) –

+5

सुनिश्चित करें, अगर आपको पठनीयता या कुछ पसंद है। – John

-2

अच्छा, यह स्पष्ट रूप से डेटा संरचना के साथ एक मुद्दा है जिसका आप उपयोग कर रहे हैं। उदाहरण के लिए हैशटेबल का प्रयोग करें। कुछ कार्यान्वयन प्रति कुंजी एकाधिक प्रविष्टियों का समर्थन करते हैं, इसलिए कोई भी नवीनतम तत्व को बंद कर सकता है, या उन सभी को हटा सकता है।

लेकिन यह है, और आप समाधान खोजने के लिए क्या जा रहे हैं, एक अलग डेटा संरचना के माध्यम से लालित्य, एल्गोरिदम नहीं। हो सकता है कि अगर आप सॉर्ट किए गए हों, या कुछ बेहतर हो, तो सूची में पुनरावृत्ति ही आपकी एकमात्र विधि है।

संपादित करें: किसी को एहसास है कि उसने 'दक्षता' के लिए कहा है ... इन सभी सुझाए गए तरीकों को सिर्फ सूची में दोहराया गया है, जो उन्होंने सुझाए गए जैसा ही है।

+1

कुछ समस्याओं के लिए, एक अलग डेटा संरचना पर स्विच करना वास्तव में एक विकल्प नहीं है - विशेष रूप से, यदि तत्वों के सेट के बाद तक आप फ़िल्टर स्थिति को नहीं जानते हैं। उदाहरण के लिए, यदि आप किसी प्रकार की खोज कर रहे हैं, और अपनी खोज स्थान को छीनना चाहते हैं, तो आप आमतौर पर अपने छंटनी के लिए उचित कटऑफ स्थिति नहीं जान पाएंगे। –

3

फ़िल्टर इसके लिए शानदार होगा। सरल उदाहरण:

names = ['mike', 'dave', 'jim'] 
filter(lambda x: x != 'mike', names) 
['dave', 'jim'] 

संपादित करें: कोरी की सूची समझ भी अद्भुत है।

10

a list comprehension

list = [x for x in list if x[-5:] != "smith"] 
+0

वास्तव में पूर्णांक के लिए काम नहीं कर रहा है। temprevengelist = "0-12354-6876" temprevengelist = temprevengelist.split ('-') सूची = [x के लिए x x temprevengelist में x x--5:]!= 6876] –

+0

@ फ़ैहिम अक्टर: ऐसा इसलिए है क्योंकि आप एक स्ट्रिंग के लिए एक पूर्णांक की तुलना कर रहे हैं: पायथन में, '6876' (int) और' 6876 "' (स्ट्रिंग) दो अलग-अलग मान हैं, और समान नहीं हैं। 'X [-5:]! = 6876' को 'x [-5:]! =" 6876 "' या 'int (x [-5:]) के साथ बदलने का प्रयास करें! = 6876' –

2

दोनों समाधान, फ़िल्टर और समझ को एक नई सूची बनाने की आवश्यकता है। मुझे यकीन है कि होने के लिए अजगर internals के लिए पर्याप्त नहीं पता है, लेकिन मुझे लगता है है कि एक और अधिक परंपरागत (लेकिन कम सुरुचिपूर्ण) दृष्टिकोण और अधिक कुशल हो सकता है:

names = ['Jones', 'Vai', 'Smith', 'Perez'] 

item = 0 
while item <> len(names): 
    name = names [item] 
    if name=='Smith': 
     names.remove(name) 
    else: 
     item += 1 

print names 

वैसे भी, कम सूचियों के लिए, मैं के साथ चिपके रहते हैं पहले प्रस्तावित दो समाधानों में से एक।

+0

मुझे लगता है कि नाम.remove (नाम) एक ओ (एन) ऑपरेशन हो सकता है, जो इसे ओ (एन^2) एल्गोरिदम बना देगा। – postfuturist

+1

मैं व्यक्तिगत रूप से अपनी अभिव्यक्ति को आइटम Miquella

+0

नाम.remove (name) से del नाम [item] या names.pop (item) का उपयोग करना शायद अधिक कुशल है। यह ओ (एन) होने की बहुत कम संभावना है, हालांकि मुझे यह पता चलता है कि यह कैसे काम करता है इसके वास्तविक आंतरिक नहीं हैं। – rjmunro

1

फिल्टर और सूची comprehensions अपने उदाहरण के लिए ठीक हैं, लेकिन वे समस्याओं की एक जोड़ी है:

  • वे अपनी सूची की एक प्रतिलिपि बनाने और नया एक लौटने के लिए, और कहा कि अक्षम हो जाएगा जब मूल सूची वास्तव में बड़ी है
  • आइटम चुनने के मानदंडों के मामले में वे वास्तव में बोझिल हो सकते हैं (आपके मामले में, यदि नाम [-5:] == 'स्मिथ') अधिक जटिल है, या कई स्थितियां हैं।

आपका मूल समाधान वास्तव में बहुत बड़ी सूचियों के लिए अधिक कुशल है, भले ही हम इसे स्वीकार कर सकें। लेकिन अगर आप चिंता आप एक से अधिक 'जॉन स्मिथ' हो सकता है, यह मूल्य पर स्थिति पर और न आधारित हटा कर ठीक किया जा सकता:

names = ['Jones', 'Vai', 'Smith', 'Perez', 'Smith'] 

toremove = [] 
for pos, name in enumerate(names): 
    if name[-5:] == 'Smith': 
     toremove.append(pos) 
for pos in sorted(toremove, reverse=True): 
    del(names[pos]) 

print names 

हम सूची के आकार पर विचार किए बिना एक समाधान नहीं चुन सकते, लेकिन बड़ी सूचियों के लिए मैं फ़िल्टर के बजाय आपके 2-पास समाधान को प्राथमिकता दूंगा या

+0

यदि आपके पास एक से अधिक 'स्मिथ' प्रविष्टि है, तो यह ठीक से काम नहीं करता है, क्योंकि निकालने के अतिरिक्त उदाहरण पहले के उदाहरणों को हटाने के कारण स्थानांतरित कर दिए गए हैं। और इसी कारण से, यह एल्गोरिदम एक अपवाद का कारण बनता है यदि सूची के अंत में दूसरा 'स्मिथ' प्रविष्टि जोड़ा जाता है। – Miquella

+0

@ मिक्सेल: आप सही हैं, मेरी मूल पोस्ट एकाधिक स्मिथ के लिए विफल रही, मैंने इसे रिवर्स ऑर्डर में हटा दिया। धन्यवाद। –

4

फ़िल्टरिंग (या तो फिल्टर या सूची समझ का उपयोग करके) काम नहीं करता है। ऐसा तब होता है जब कोई अन्य ऑब्जेक्ट उस सूची का संदर्भ रखता है जिसे आप संशोधित कर रहे हैं और आपको सूची में सूची को संशोधित करने की आवश्यकता है।

for name in names[:]: 
    if name[-5:] == 'Smith': 
     names.remove(name) 

मूल कोड से फर्क सिर्फ इतना है पाश के लिए में names[:] बजाय names का उपयोग है। इस तरह कोड सूची की एक (उथली) प्रतिलिपि पर पुनरावृत्त करता है और निष्कासन अपेक्षा के अनुसार काम करता है। चूंकि सूची की प्रतिलिपि उथली है, यह काफी तेज़ है।

2

शब्दकोशों के साथ काम करने के बारे में अपने प्रश्न का उत्तर के लिए, आपको ध्यान रखना चाहिए कि अजगर 3.0 शामिल dict comprehensions देगा: इस तरह से

>>> {i : chr(65+i) for i in range(4)} 

मतलब समय में, आप एक अर्ध dict समझ कर सकते हैं:

>>> dict([(i, chr(65+i)) for i in range(4)]) 

या एक और अधिक प्रत्यक्ष जवाब के रूप में:

dict([(key, name) for key, name in some_dictionary.iteritems if name[-5:] != 'Smith']) 
+0

आपको जनरेटर अभिव्यक्तियों के आस-पास '() 'रखने की आवश्यकता नहीं है जब तक कि वे एकमात्र तर्क न हों और' [] 'जनरेटर अभिव्यक्ति को एक सूची को मूर्त रूप देने के लिए बनाता है जो' dict ([(k, v) के लिए बनाता है , v.items() में v) ''' dict (i (k, v) के लिए बहुत धीमी है, डी के लिए v, v.items() में)) –

37

आप आईटीईआर भी कर सकते हैं सूची पर पीछे की ओर खाया:

for name in reversed(names): 
    if name[-5:] == 'Smith': 
     names.remove(name) 

यह (जैसे filter या एक सूची समझ) लाभ यह है कि यह एक नई सूची का निर्माण नहीं करता है और एक सूची प्रतिलिपि ([:] की तरह) के बजाय एक iterator उपयोग करता है।

ध्यान दें कि हालांकि पीछे की ओर इशारा करते समय तत्वों को हटा देना सुरक्षित है, उन्हें डालने में कुछ हद तक मुश्किल है।

+0

मेरी समस्या हल हो गई, धन्यवाद :) – Ashy

+0

यह वास्तव में है अभिनव और पायथनिक समाधान। मुझे यह पसंद है! – richo

+0

ओओ सुंदर चालाक – Claudiu

1

एक सेट के मामले में।

toRemove = set([]) 
for item in mySet: 
    if item is unwelcome: 
     toRemove.add(item) 
mySets = mySet - toRemove 
28

स्पष्ट जवाब एक ही है कि जॉन और कुछ अन्य लोगों को दे दी है, अर्थात् है:

>>> names = [name for name in names if name[-5:] != "Smith"]  # <-- slower 

लेकिन उस नुकसान यह है कि यह एक नई सूची वस्तु बनाता है, बल्कि मूल वस्तु पुन: उपयोग की तुलना में है । मैं कुछ रूपरेखा और प्रयोग किया है, और सबसे कारगर विधि मैं के साथ आया है:

>>> names[:] = (name for name in names if name[-5:] != "Smith") # <-- faster 

को नियत "नाम [:]" मूल रूप से मतलब है "निम्न मान के साथ नाम सूची की सामग्री को बदलने"। यह केवल नामों को असाइन करने से अलग है, जिसमें यह एक नई सूची वस्तु नहीं बनाता है। असाइनमेंट का दायां हाथ एक जनरेटर अभिव्यक्ति है (स्क्वायर ब्रैकेट के बजाए ब्रांड्स का उपयोग नोट करें)। यह पाइथन सूची में फिर से शुरू करने का कारण बन जाएगा।

कुछ त्वरित प्रोफाइलिंग से पता चलता है कि यह सूची समझ दृष्टिकोण से लगभग 30% तेज है, और फ़िल्टर दृष्टिकोण से लगभग 40% तेज है।

कैविट: जबकि यह समाधान स्पष्ट समाधान से तेज़ है, यह अधिक अस्पष्ट है, और अधिक उन्नत पायथन तकनीकों पर निर्भर करता है। यदि आप इसका उपयोग करते हैं, तो मैं इसे एक टिप्पणी के साथ अनुशंसा करता हूं। यह शायद उन मामलों में उपयोग करने लायक है जहां आप वास्तव में इस विशेष ऑपरेशन के प्रदर्शन की परवाह करते हैं (जो कि बहुत तेज़ है चाहे कोई फर्क नहीं पड़ता)। (जिस मामले में मैंने इसका इस्तेमाल किया, मैं ए * बीम खोज कर रहा था, और खोज बीम से खोज बिंदुओं को हटाने के लिए इसका इस्तेमाल किया।)

+2

वास्तव में दिलचस्प प्रदर्शन खोज। क्या आप अपने प्रोफाइलिंग पर्यावरण और मूल्यांकन विधियों के बारे में अधिक जानकारी दे सकते हैं? – Drake

+0

मैं शर्त लगाता हूं कि आप प्रत्येक पुनरावृत्ति को स्लाइस बनाने के बजाय 'name.endswith ('Smith') 'का उपयोग करके इसे और भी तेज़ बना सकते हैं। किसी भी तरह से, यह जानकारी का एक मूल्यवान टुकड़ा है जिसे शायद कभी नहीं मिला अगर यह आपके उत्तर के लिए नहीं था, धन्यवाद। –

+1

'नाम [:] 'सुझाव' os.walk' के साथ उपयोग करने के लिए विशेष रूप से सहायक था, पीछे हटने वाले आइटमों को हटाने के लिए – wowest

2

यदि सूची को जगह में फ़िल्टर किया जाना चाहिए और सूची का आकार काफी बड़ा है , तो पिछले उत्तरों में वर्णित एल्गोरिदम, जो list.remove() पर आधारित हैं, अनुपयुक्त हो सकते हैं, क्योंकि उनकी कम्प्यूटेशनल जटिलता ओ (एन^2) है। इस मामले में आप निम्नलिखित नहीं तो pythonic फ़ंक्शन का उपयोग कर सकते हैं:

def filter_inplace(func, original_list): 
    """ Filters the original_list in-place. 

    Removes elements from the original_list for which func() returns False. 

    Algrithm's computational complexity is O(N), where N is the size 
    of the original_list. 
    """ 

    # Compact the list in-place. 
    new_list_size = 0 
    for item in original_list: 
    if func(item): 
     original_list[new_list_size] = item 
     new_list_size += 1 

    # Remove trailing items from the list. 
    tail_size = len(original_list) - new_list_size 
    while tail_size: 
    original_list.pop() 
    tail_size -= 1 


a = [1, 2, 3, 4, 5, 6, 7] 

# Remove even numbers from a in-place. 
filter_inplace(lambda x: x & 1, a) 

# Prints [1, 3, 5, 7] 
print a 

संपादित करें: वास्तव में, https://stackoverflow.com/a/4639748/274937 पर समाधान मेरा समाधान के लिए बेहतर है। यह अधिक पायथन है और तेजी से काम करता है। स्वतंत्र रूप से इस पेज को खोजने से पहले

def filter_inplace(func, original_list): 
    """ Filters the original_list inplace. 

    Removes elements from the original_list for which function returns False. 

    Algrithm's computational complexity is O(N), where N is the size 
    of the original_list. 
    """ 
    original_list[:] = [item for item in original_list if func(item)] 
+0

को पार करने के लिए डायनाम नाम फ़िल्टर करने के लिए:' del original_list [new_list_size:] ' – jfs

1

यहाँ मेरी filter_inplace कार्यान्वयन कि यथा-स्थान एक सूची से आइटम फिल्टर करने के लिए इस्तेमाल किया जा सकता है, मैं अपने दम पर इस के साथ आया था: तो, यहाँ एक नया filter_inplace() कार्यान्वयन है । यह एक ही एल्गोरिदम है जैसा कि पाब्लोग ने पोस्ट किया है, बस अधिक सामान्य बना दिया है ताकि आप इसे सूचियों को फ़िल्टर करने के लिए उपयोग कर सकें,के आधार पर सूची से निकालने में भी सक्षम है यदि True को उलट दिया गया है; अगर आप करेंगे तो एक प्रकार का उलट फ़िल्टर।

def filter_inplace(conditionFunc, list, reversed=False): 
    index = 0 
    while index < len(list): 
     item = list[index] 

     shouldRemove = not conditionFunc(item) 
     if reversed: shouldRemove = not shouldRemove 

     if shouldRemove: 
      list.remove(item) 
     else: 
      index += 1 
संबंधित मुद्दे