ما هي طريقة بايثون لاكتشاف العنصر الأخير في حلقة بايثون "لأجل"؟

StackOverflow https://stackoverflow.com/questions/1630320

سؤال

أرغب في معرفة أفضل طريقة (طريقة أكثر إحكاما و"بيثونية") لإجراء معالجة خاصة للعنصر الأخير في حلقة for.هناك جزء من التعليمات البرمجية يجب استدعاؤه فقط بين العناصر، التي يتم قمعها في آخر واحد.

إليك كيف أفعل ذلك حاليًا:

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

هل كانت مفيدة؟

المحلول

وأكثر من مرة أنه من الأسهل (وأرخص) لجعل <م> أولا تكرار الحالة الخاصة بدلا من واحد آخر:

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

    item()

وهذا سيعمل على أي iterable، حتى بالنسبة لأولئك الذين ليس لديهم 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، وغيرهم ممن يحتاجون حقا لمعرفة ما إذا كانت القيمة الحالية للiterable دون 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

نصائح أخرى

"الرمز بين" هو مثال على رئيس الذيل نمط.

لديك عنصر، يتبعه تسلسل من أزواج (بين، العنصر).يمكنك أيضًا عرض هذا كتسلسل لأزواج (العنصر، بين) متبوعًا بعنصر.من الأسهل عمومًا اعتبار العنصر الأول خاصًا وجميع العناصر الأخرى باعتبارها الحالة "القياسية".

علاوة على ذلك، لتجنب تكرار التعليمات البرمجية، يتعين عليك توفير وظيفة أو كائن آخر يحتوي على التعليمات البرمجية التي لا تريد تكرارها.تضمين ان لو عبارة في حلقة تكون دائمًا خاطئة باستثناء مرة واحدة هي نوع من السخافة.

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 )

يعد هذا أكثر موثوقية لأنه أسهل قليلاً في الإثبات، ولا ينشئ بنية بيانات إضافية (أي نسخة من القائمة) ولا يتطلب الكثير من التنفيذ الضائع لو الشرط الذي هو دائما كاذبة إلا مرة واحدة.

إذا كنت مجرد النظر الى تعديل العنصر الأخير في data_list ثم يمكنك ببساطة استخدام التدوين:

L[-1]

ولكن، يبدو أنك تفعل أكثر من ذلك. فلا حرج حقا مع طريقك. حتى أخذت نظرة سريعة على بعض بفك كود للحصول على علامات القالب، وأنها تفعل أساسا ما تفعلونه.

وعلى الرغم من أن مسألة قديمة جدا، وأنا جئت إلى هنا عن طريق البريد الإلكتروني، ولقد وجدت طريقة بسيطة للغاية: قائمة التقطيع. دعونا نقول لكم تريد وضع '&' بين كل مقالات القائمة.

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

وهذا يعود "1 و 2 و 3".

وهذا هو مماثل لنهج النمل Aasma ولكن دون استخدام وحدة itertools. كما انها مكرر متخلفة والتي تبدو الاخضر عنصر واحد في مجرى مكرر:

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

وإذا كانت العناصر هي فريدة من نوعها:

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

ويمكنك استخدام نافذة انزلاق خلال إدخال البيانات للحصول على نظرة خاطفة على القيمة التالية واستخدام الحارس للكشف عن القيمة الأخيرة. وهذا يعمل على أي iterable، لذلك لا تحتاج إلى معرفة طول مسبقا. تنفيذ البشرى هو من itertools صفات .

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

هل هناك إمكانية لتكرار عبر جميع ولكن العنصر الأخير، وعلاج خارج الأخير من الحلقة؟ بعد كل شيء، وخلق حلقة أن تفعل شيئا مشابها لجميع العناصر التي حلقة أكثر؛ إذا عنصر واحد يحتاج إلى شيء خاص، لا ينبغي أن يكون في الحلقة.

و(انظر أيضا هذا السؤال: لا -The-الأخير عنصر-في-حلقة تستحق واحد في منفصلة المعاملة )

وتحرير: منذ والسؤال هو أكثر حول "بين"، إما <م> أولا عنصر واحد هو خاص لأنه لا يوجد لديه سلف، أو <م> آخر العنصر هو خاص لأنه ليس لديه خليفة.

وهناك شيء خاطئ مع طريقك، إلا إذا سيكون لديك 100 000 الحلقات ويريد إنقاذ 100 000 "إذا" البيانات. في هذه الحالة، يمكن أن تذهب بهذه الطريقة:

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

إذا يشعر مثل أود أن تفعل ذلك بهذه الطريقة، يبدو بسيطا بالنسبة لي.

واستخدام التقطيع و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>

<القوي> مسؤولية المشتري : لهذا يعمل فقط إذا كان كل العناصر في القائمة هي مختلفة في الواقع (لديها مواقع مختلفة في الذاكرة). تحت غطاء محرك السيارة، قد بيثون كشف عناصر المتساوية وإعادة استخدام نفس الأشياء بالنسبة لهم. على سبيل المثال، سلاسل من نفس القيمة والأعداد الصحيحة المشتركة.

لقد أوصلني 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()

يمكنك حتى استخدام بينما لين (element_list) إذا لم تكن بحاجة إلى فعل أي شيء مع العنصر الأخير.أجد هذا الحل أكثر أناقة من التعامل مع التالي ().

وأنا أحب اقتراب @ إيثان تي، ولكن while True أمر خطير من وجهة نظري.

while L:
    e = L.pop(0)
    # process element
    if not L:
        print('Last element has been detected.')

وتأخير في معالجة خاصة من العنصر الأخير إلى ما بعد الحلقة.

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

وعلى افتراض الإدخال كما مكرر، وهنا طريقة استخدام نقطة الإنطلاق وizip من itertools:

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

وإذا كنت تمر القائمة، بالنسبة لي هذا العمل أيضا:

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

والحل أبسط القادمة الى ذهني هو:

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

وذلك أتوقع النفقات العامة إلى أن يكون غير قابل للانتخاب.

وعدد البنود مرة واحدة ومواكبة عدد من البنود المتبقية:

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

وبهذه الطريقة يمكنك تقييم فقط طول قائمة واحدة. يبدو أن العديد من الحلول في هذه الصفحة لتولي طول غير متوفر مسبقا، ولكن هذا ليس جزءا من سؤالك. إذا كان لديك طول، واستخدامها.

وبالنسبة لي الطريقة الأكثر بسيطة وpythonic للتعامل مع حالة خاصة في نهاية القائمة:

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

وبطبيعة الحال هذه يمكن أن تستخدم أيضا لعلاج العنصر الأول بطريقة خاصة.

ويمكن أن تكون هناك طرق متعددة. سوف تشريح يكون أسرع. إضافة واحدة أكثر مما يستخدم .index () الأسلوب:

>>> l1 = [1,5,2,3,5,1,7,43]                                                 
>>> [i for i in l1 if l1.index(i)+1==len(l1)]                               
[43]
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top