एक 'लूप' के लिए अंतिम तत्व का पता लगाने का पायथोनिक तरीका क्या है?


187

मैं लूप के लिए अंतिम तत्व के लिए एक विशेष उपचार करने के लिए सबसे अच्छा तरीका (अधिक कॉम्पैक्ट और "पाइथोनिक" तरीका) जानना चाहता हूं। कोड का एक टुकड़ा है जिसे केवल तत्वों के बीच कहा जाना चाहिए , आखिरी में दबाया जाना चाहिए ।

यहाँ है कि मैं वर्तमान में यह कैसे करूँ:

for i, data in enumerate(data_list):
    code_that_is_done_for_every_element
    if i != len(data_list) - 1:
        code_that_is_done_between_elements

क्या कोई बेहतर तरीका है?

नोट: मैं इसे हैक जैसे प्रयोग से नहीं बनाना चाहता reduce;)


वह पहले वाले के बारे में क्या? क्या इसे भी दबा दिया जाना चाहिए?
एडम मतान

क्या आप हमें बता सकते हैं कि यह तत्वों के बीच क्या किया जा रहा है?
साइलेंटगॉस्ट

2
मैं एक सामान्य मामले के लिए उत्तर प्राप्त करना चाहता हूं, लेकिन एक ठोस मामला जहां मुझे इसकी आवश्यकता है, एक धारा पर चीजें लिख रहे हैं, उनके बीच विभाजकों के साथ, जैसे stream.write (',' .join (name_list))। लेकिन स्ट्रिंग्स को
समेटे


इस उत्तर की पहली तीन पंक्तियों ने वास्तव में मेरी मदद की, एक समान चुनौती थी।
इलायची

जवाबों:


151

पिछली बार के बजाय पहले पुनरावृत्ति को विशेष मामला बनाने के लिए यह सबसे आसान (और सस्ता) है :

first = True
for data in data_list:
    if first:
        first = False
    else:
        between_items()

    item()

यह किसी भी चलने के लिए काम करेगा, यहां तक ​​कि उन लोगों के लिए len(): जिनके पास नहीं है :

file = open('/path/to/file')
for line in file:
    process_line(line)

    # No way of telling if this is the last line!

इसके अलावा, मुझे नहीं लगता कि आम तौर पर बेहतर समाधान है क्योंकि यह इस बात पर निर्भर करता है कि आप क्या करने की कोशिश कर रहे हैं। उदाहरण के लिए, यदि आप किसी सूची से एक स्ट्रिंग का निर्माण कर रहे हैं, तो यह स्वाभाविक रूप str.join()से for"विशेष मामले के साथ" लूप का उपयोग करने से बेहतर है ।


एक ही सिद्धांत लेकिन अधिक कॉम्पैक्ट का उपयोग करना:

for i, line in enumerate(data_list):
    if i > 0:
        between_items()
    item()

परिचित लगता है, है ना? :)


@Ofko के लिए, और अन्य जिन्हें वास्तव में यह पता लगाने की आवश्यकता है कि क्या बिना चलने योग्य चलने का वर्तमान मूल्य len()अंतिम है, आपको आगे देखने की आवश्यकता होगी:

def lookahead(iterable):
    """Pass through all values from the given iterable, augmented by the
    information if there are more values to come after the current one
    (True), or if it is the last value (False).
    """
    # Get an iterator and pull the first value.
    it = iter(iterable)
    last = next(it)
    # Run the iterator to exhaustion (starting from the second value).
    for val in it:
        # Report the *previous* value (more to come).
        yield last, True
        last = val
    # Report the last value.
    yield last, False

तो आप इसे इस तरह से उपयोग कर सकते हैं:

>>> for i, has_more in lookahead(range(3)):
...     print(i, has_more)
0 True
1 True
2 False

1
सच है, इस तरह से मेरा बेहतर लगता है, कम से कम यह enumerate और लेन का उपयोग करने की जरूरत नहीं है।
e.tadeu

हां, लेकिन यह एक और जोड़ता है ifजिससे बचा जा सकता था यदि लूप को दो छोरों में विभाजित किया गया था। हालाँकि, यह तब ही प्रासंगिक होता है जब किसी विशाल डेटा सूची को पुनरावृत्त किया जाता है।
एडम मटन

दो छोरों में विभाजित होने के साथ समस्या यह है कि यह या तो DRY का उल्लंघन करता है या यह आपको तरीकों को परिभाषित करने के लिए मजबूर करता है।
e.tadeu

मैं वास्तव में आपके पिछले उदाहरण को समझने की कोशिश करता हूं (जो मेरे कोड में त्रुटिपूर्ण रूप से काम करता है), लेकिन मुझे समझ में नहीं आता कि यह कैसे काम करता है (विचार के पीछे)
ओलिवियर पोंस

1
@OlivierPons आपको पायथन के पुनरावृत्त प्रोटोकॉल को समझने की आवश्यकता है: मुझे एक वस्तु के लिए एक पुनरावृत्ति मिलता है, और पहले मान को पुनः प्राप्त करता है next()। फिर मैं शोषण करता हूं कि एक पुनरावृत्ति अपने आप में forचलने योग्य है, इसलिए मैं इसे थकावट तक लूप में उपयोग कर सकता हूं , दूसरे से अंतिम मूल्य तक पुनरावृत्ति। इस दौरान मैं स्थानीय रूप से पुनरावृत्तिकर्ता से वर्तमान मूल्य को प्राप्त करता हूं और yieldइसके बजाय पिछले एक को रखता हूं । इस तरह मुझे पता है कि आने के लिए एक और मूल्य है। लूप के बाद, मैंने हर मूल्य की सूचना दी है, लेकिन पिछले एक।
फर्डिनेंड बेयर

20

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

s = ""
l = [1, 2, 3]
for i in l[:-1]:
    s = s + str(i) + ' & '
s = s + str(l[-1])

यह '1 और 2 और 3' देता है।


7
आपने अभी-अभी ज्वाइन फंक्शन को फिर से लागू किया है: `" और ".Join ([str (x) in x in l])
ब्रायन ओकले

स्ट्रिंग संघनन कुछ अक्षम है। यदि len(l)=1000000इस उदाहरण में, प्रोग्राम थोड़ी देर के लिए चलेगा। appendकी सिफारिश की गई है l=[1,2,3]; l.append(4);
plhn

18

'कोड के बीच' हेड-टेल पैटर्न का एक उदाहरण है ।

आपके पास एक आइटम है, जो कि (बीच, आइटम) जोड़े के अनुक्रम के बाद है। आप इसे एक आइटम के बाद (आइटम के बीच) जोड़े के अनुक्रम के रूप में भी देख सकते हैं। आम तौर पर पहला तत्व विशेष और सभी अन्य को "मानक" मामले के रूप में लेना सरल है।

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

def item_processing( item ):
    # *the common processing*

head_tail_iter = iter( someSequence )
head = head_tail_iter.next()
item_processing( head )
for item in head_tail_iter:
    # *the between processing*
    item_processing( item )

यह अधिक विश्वसनीय है क्योंकि यह साबित करना थोड़ा आसान है, यह एक अतिरिक्त डेटा संरचना (अर्थात, किसी सूची की एक प्रति) नहीं बनाता है और यदि एक बार को छोड़कर हमेशा गलत होता है , तो एक व्यर्थ निष्पादन के बहुत सारे निष्पादन की आवश्यकता नहीं होती है ।


4
फंक्शन कॉल्स वैसे तो धीमी होती हैं, ifइसलिए "बर्बाद निष्पादन" तर्क नहीं देता है।
फर्डिनेंड Beyer

1
मुझे यकीन नहीं है कि फ़ंक्शन कॉल और यदि-स्टेटमेंट के बीच गति अंतर क्या कुछ भी करना है। मुद्दा यह है कि इस फॉर्मूलेशन का कोई इफ-स्टेटमेंट नहीं है जो हमेशा झूठा हो (एक बार को छोड़कर।)
एस.लॉट

1
मैंने आपके कथन "... की व्याख्या की है और यदि एक बार को छोड़कर" "के रूप में एक बार को छोड़कर जो कि हमेशा गलत है, तो इसे निष्पादित करने की बहुत आवश्यकता होती है, क्योंकि यह एक जोड़े को बचाता है if"। जाहिर है आप सिर्फ "कोड स्वच्छता" का उल्लेख कर रहे हैं?
फर्डिनेंड बायर 18

क्या ifपायथन समुदाय द्वारा वास्तव में क्लीनर माने जाने वाले बयान का उपयोग करने के बजाय एक फ़ंक्शन को परिभाषित करना है ?
मार्कस वॉन ब्रोडी

17

यदि आप अंतिम तत्व को संशोधित करना data_listचाहते हैं तो आप केवल अंकन का उपयोग कर सकते हैं:

L[-1]

हालाँकि, ऐसा लगता है कि आप इससे अधिक कर रहे हैं। आपके रास्ते में वास्तव में कुछ भी गलत नहीं है। मैंने उनके टेम्पलेट टैग के लिए कुछ Django कोड पर एक त्वरित नज़र डाली और वे मूल रूप से वही कर रहे हैं जो आप कर रहे हैं।


1
मैं इसे संशोधित नहीं कर रहा हूँ, मैं इसे कुछ करने के लिए उपयोग कर रहा हूँ
e.tadeu

4
@ e.tadeu इससे कोई फर्क नहीं पड़ता कि आप इसे संशोधित कर रहे हैं या नहीं। यदि आपका कथन बदलना: if data != datalist[-1]:और बाकी सब कुछ उसी तरह रखना, जो मेरी राय में इसे कोड करने का सबसे अच्छा तरीका होगा।
२०:२

8
@spacetyper यह तब टूटता है जब अंतिम मान गैर-अनन्य होता है।
-कुन

14

यदि आइटम अद्वितीय हैं:

for x in list:
    #code
    if x == list[-1]:
        #code

अन्य विकल्प:

pos = -1
for x in list:
    pos += 1
    #code
    if pos == len(list) - 1:
        #code


for x in list:
    #code
#code - e.g. print x


if len(list) > 0:
    for x in list[:-1]
        #code
    for x in list[-1]:
        #code

10

यह चींटियों के प्लाज्मा के दृष्टिकोण के समान है, लेकिन इटर्टूलस मॉड्यूल का उपयोग किए बिना। यह एक लैगिंग इटरेटर भी है जो इटरेटर स्ट्रीम में एकल तत्व को आगे-आगे देखता है:

def last_iter(it):
    # Ensure it's an iterator and get the first field
    it = iter(it)
    prev = next(it)
    for item in it:
        # Lag by one item so I know I'm not at the end
        yield 0, prev
        prev = item
    # Last item
    yield 1, prev

def test(data):
    result = list(last_iter(data))
    if not result:
        return
    if len(result) > 1:
        assert set(x[0] for x in result[:-1]) == set([0]), result
    assert result[-1][0] == 1

test([])
test([1])
test([1, 2])
test(range(5))
test(xrange(4))

for is_last, item in last_iter("Hi!"):
    print is_last, item

4

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

from itertools import tee, izip, chain

def pairwise(seq):
    a,b = tee(seq)
    next(b, None)
    return izip(a,b)

def annotated_last(seq):
    """Returns an iterable of pairs of input item and a boolean that show if
    the current item is the last item in the sequence."""
    MISSING = object()
    for current_item, next_item in pairwise(chain(seq, [MISSING])):
        yield current_item, next_item is MISSING:

for item, is_last_item in annotated_last(data_list):
    if is_last_item:
        # current item is the last item

3

क्या सभी-लेकिन अंतिम तत्व पर पुनरावृति की संभावना नहीं है, और लूप के बाहर पिछले एक का इलाज करें? आखिरकार, आपके द्वारा लूप किए जाने वाले सभी तत्वों के समान कुछ करने के लिए एक लूप बनाया जाता है; यदि किसी तत्व को कुछ विशेष चाहिए, तो वह लूप में नहीं होना चाहिए।

(यह प्रश्न भी देखें: क्या-आखिर-तत्व-में-एक-लूप-पात्र-अलग-अलग-उपचार )

EDIT: चूंकि प्रश्न "बीच में" के बारे में अधिक है, इसलिए या तो पहला तत्व विशेष है कि इसमें कोई पूर्ववर्ती नहीं है, या अंतिम तत्व इसमें विशेष है कि इसका कोई उत्तराधिकारी नहीं है।


लेकिन अंतिम तत्व को सूची में हर दूसरे तत्व के समान माना जाना चाहिए। समस्या वह चीज है जिसे केवल तत्वों के बीच किया जाना चाहिए ।
e.tadeu

उस मामले में, पहले वाला एक पूर्ववर्ती के बिना एकमात्र है। उस एक को अलग करें, और शेष सूची सामान्य कोड पर लूप करें।
xtofl

3

मुझे @ एथन-टी का दृष्टिकोण पसंद है, लेकिन while Trueमेरे दृष्टिकोण से खतरनाक है।

data_list = [1, 2, 3, 2, 1]  # sample data
L = list(data_list)  # destroy L instead of data_list
while L:
    e = L.pop(0)
    if L:
        print(f'process element {e}')
    else:
        print(f'process last element {e}')
del L

यहां, data_listऐसा इसलिए है कि अंतिम तत्व सूची के पहले एक के मूल्य के बराबर है। एल के साथ आदान-प्रदान किया जा सकता है data_listलेकिन इस मामले में यह लूप के बाद खाली होता है। while Trueयह भी उपयोग करना संभव है कि क्या आप जांचते हैं कि सूची प्रसंस्करण से पहले खाली नहीं है या चेक की आवश्यकता नहीं है (ouch!)।

data_list = [1, 2, 3, 2, 1]
if data_list:
    while True:
        e = data_list.pop(0)
        if data_list:
            print(f'process element {e}')
        else:
            print(f'process last element {e}')
            break
else:
    print('list is empty')

अच्छी बात यह है कि यह तेज है। बुरा - यह विनाशकारी है (data_list खाली हो जाता है)।

सबसे सहज समाधान:

data_list = [1, 2, 3, 2, 1]  # sample data
for i, e in enumerate(data_list):
    if i != len(data_list) - 1:
        print(f'process element {e}')
    else:
        print(f'process last element {e}')

अरे हाँ, आपने पहले ही इसका प्रस्ताव रखा है!


2

आपके रास्ते में कुछ भी गलत नहीं है, जब तक कि आपके पास 100 000 लूप नहीं होंगे और यदि आप 100 "स्टेटमेंट" सेव करना चाहते हैं। उस स्थिति में, आप इस तरह से जा सकते हैं:

iterable = [1,2,3] # Your date
iterator = iter(iterable) # get the data iterator

try :   # wrap all in a try / except
    while 1 : 
        item = iterator.next() 
        print item # put the "for loop" code here
except StopIteration, e : # make the process on the last element here
    print item

आउटपुट:

1
2
3
3

लेकिन वास्तव में, आपके मामले में मुझे ऐसा लग रहा है कि यह ओवरकिल है।

किसी भी मामले में, आप शायद फिसलने के साथ भाग्यशाली होंगे:

for item in iterable[:-1] :
    print item
print "last :", iterable[-1]

#outputs
1
2
last : 3

या केवल :

for item in iterable :
    print item
print iterable[-1]

#outputs
1
2
3
last : 3

अंत में, एक KISS जिस तरह से आप सामान करना है, और कहा कि, किसी भी iterable साथ काम करेगा बिना भी शामिल होते हैं __len__:

item = ''
for item in iterable :
    print item
print item

ouputs:

1
2
3
3

अगर मुझे लगता है कि मैं इसे इस तरह से करूंगा, तो मुझे सरल लगेगा।


2
लेकिन ध्यान दें कि चलने योग्य [-1] सभी पुनरावृत्तियों पर काम नहीं करेगा (जैसे जनरेटर जिसमें लेन नहीं है )
e.tadeu

यदि आप चाहते हैं कि लूप के बाद अंतिम आइटम का उपयोग करना है, तो itemइसका उपयोग करके पुन: गणना करने के बजाय बस का उपयोग करें list[-1]। लेकिन फिर भी: मुझे नहीं लगता कि यह वही है जो ओपी के लिए पूछ रहा था, क्या यह था?
फर्डिनेंड बायर

पुन: iterable.__iter__() - कृपया __कार्यों को सीधे कॉल न करें । होना चाहिए iter(iterable)
पॉलमैक्स

2

isअंतिम तत्व की जांच के लिए स्लाइसिंग का उपयोग करें :

for data in data_list:
    <code_that_is_done_for_every_element>
    if not data is data_list[-1]:
        <code_that_is_done_between_elements>

कैविट एम्प्टर : यह केवल तभी काम करता है जब सूची के सभी तत्व वास्तव में अलग-अलग हों (स्मृति में अलग-अलग स्थान हों)। हुड के तहत, पायथन समान तत्वों का पता लगा सकता है और उनके लिए समान वस्तुओं का पुन: उपयोग कर सकता है। उदाहरण के लिए, समान मूल्य और सामान्य पूर्णांकों के तार के लिए।


2

यदि आप सूची से गुजर रहे हैं, तो मेरे लिए यह भी काम किया है:

for j in range(0, len(Array)):
    if len(Array) - j > 1:
        notLast()

2

Google मुझे इस पुराने प्रश्न पर लाया और मुझे लगता है कि मैं इस समस्या के लिए एक अलग दृष्टिकोण जोड़ सकता हूं।

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

while True:
    element = element_list.pop(0)
    do_this_for_all_elements()
    if not element:
        do_this_only_for_last_element()
        break
    do_this_for_all_elements_but_last()

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


2

मेरे लिए एक सूची के अंत में एक विशेष मामले को संभालने का सबसे सरल और पुरातन तरीका है:

for data in data_list[:-1]:
    handle_element(data)
handle_special_element(data_list[-1])

बेशक इसका उपयोग पहले तत्व को एक विशेष तरीके से इलाज करने के लिए भी किया जा सकता है।


2

गिनने के बजाय, आप नीचे भी गिन सकते हैं:

  nrToProcess = len(list)
  for s in list:
    s.doStuff()
    nrToProcess -= 1
    if nrToProcess==0:  # this is the last one
      s.doSpecialStuff()

1

लूप के बाद तक अंतिम आइटम की विशेष हैंडलिंग में देरी।

>>> for i in (1, 2, 3):
...     pass
...
>>> i
3

1

इसके कई तरीके हो सकते हैं। स्लाइसिंग सबसे तेज़ होगी। एक और जोड़ने का उपयोग करता है जो .index () विधि:

>>> l1 = [1,5,2,3,5,1,7,43]                                                 
>>> [i for i in l1 if l1.index(i)+1==len(l1)]                               
[43]

0

इटरेटर के रूप में इनपुट को मानते हुए, यहाँ एक तरीका है जो इटर्लस से टी और izip का उपयोग करता है:

from itertools import tee, izip
items, between = tee(input_iterator, 2)  # Input must be an iterator.
first = items.next()
do_to_every_item(first)  # All "do to every" operations done to first item go here.
for i, b in izip(items, between):
    do_between_items(b)  # All "between" operations go here.
    do_to_every_item(i)  # All "do to every" operations go here.

डेमो:

>>> def do_every(x): print "E", x
...
>>> def do_between(x): print "B", x
...
>>> test_input = iter(range(5))
>>>
>>> from itertools import tee, izip
>>>
>>> items, between = tee(test_input, 2)
>>> first = items.next()
>>> do_every(first)
E 0
>>> for i,b in izip(items, between):
...     do_between(b)
...     do_every(i)
...
B 0
E 1
B 1
E 2
B 2
E 3
B 3
E 4
>>>

0

मेरे दिमाग में आने वाला सबसे सरल उपाय है:

for item in data_list:
    try:
        print(new)
    except NameError: pass
    new = item
print('The last item: ' + str(new))

इसलिए हम हमेशा एक आइटम को संसाधित करने में देरी करते हुए आगे की ओर देखते हैं। पहले पुनरावृत्ति के दौरान कुछ करने के लिए मैं बस त्रुटि को पकड़ता हूं।

निश्चित रूप से आपको कुछ सोचने की जरूरत है, क्रम में NameError आप जब चाहें तब उठाया जा सके।

इसके अलावा `counstruct रखें

try:
    new
except NameError: pass
else:
    # continue here if no error was raised

यह निर्भर करता है कि नया नाम पहले परिभाषित नहीं किया गया था। यदि आप पागल हैं तो आप यह सुनिश्चित कर सकते हैं कि newइसका उपयोग मौजूद नहीं है:

try:
    del new
except NameError:
    pass

वैकल्पिक रूप से आप निश्चित रूप से एक if स्टेटमेंट ( if notfirst: print(new) else: notfirst = True) का उपयोग कर सकते हैं । लेकिन जहां तक ​​मुझे पता है कि ओवरहेड बड़ा है।


Using `timeit` yields:

    ...: try: new = 'test' 
    ...: except NameError: pass
    ...: 
100000000 loops, best of 3: 16.2 ns per loop

इसलिए मैं उम्मीद करता हूं कि ओवरहेड अकल्पनीय होगा।


0

एक बार आइटम की गणना करें और शेष मदों की संख्या के साथ रखें:

remaining = len(data_list)
for data in data_list:
    code_that_is_done_for_every_element

    remaining -= 1
    if remaining:
        code_that_is_done_between_elements

इस तरह आप केवल एक बार सूची की लंबाई का मूल्यांकन करते हैं। इस पृष्ठ के कई समाधान यह मानते हैं कि लंबाई पहले से उपलब्ध नहीं है, लेकिन यह आपके प्रश्न का हिस्सा नहीं है। यदि आपके पास लंबाई है, तो इसका उपयोग करें।


0

एक सरल समाधान जो मन में आता है वह होगा:

for i in MyList:
    # Check if 'i' is the last element in the list
    if i == MyList[-1]:
        # Do something different for the last
    else:
        # Do something for all other elements

एक काउंटर का उपयोग करके एक दूसरा समान रूप से सरल समाधान प्राप्त किया जा सकता है:

# Count the no. of elements in the list
ListLength = len(MyList)
# Initialize a counter
count = 0

for i in MyList:
    # increment counter
    count += 1
    # Check if 'i' is the last element in the list
    # by using the counter
    if count == ListLength:
        # Do something different for the last
    else:
        # Do something for all other elements
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.