Python yineleyiciden son öğeyi almanın en temiz yolu


110

Python 2.6'da bir yineleyiciden son öğeyi almanın en iyi yolu nedir? Örneğin, söyle

my_iter = iter(range(5))

Almanın en kısa-kodu / en temiz yolu nedir 4dan my_iter?

Bunu yapabilirim, ancak pek verimli görünmüyor:

[x for x in my_iter][-1]

4
Yineleyiciler, öğeler arasında yineleme yapmak istediğinizi ve son öğelere gerçekten erişmek istemediğinizi varsayar. Sizi yalnızca aralık (5) [- 1] kullanmaktan alıkoyan nedir?
Frank

7
@Frank - Gerçek yineleyicinin daha karmaşık ve / veya daha uzakta ve / veya kontrolünün daha zor olduğunu varsaydımiter(range(5))
Chris Lutz

3
@Frank: Yineleyiciyi sağlayan aslında çok daha karmaşık bir jeneratör işlevi olduğu gerçeği. Bu örneği, neler olduğunu basit ve açık olsun diye uydurdum.
Peter

4
Bir yineleyicinin son öğesini istiyorsanız, büyük bir olasılıkla yanlış bir şey yapıyor olabilirsiniz. Ancak yanıt, yineleyici aracılığıyla yinelemenin gerçekten daha net bir yolu olmadığıdır. Bunun nedeni, yineleyicilerin bir boyuta sahip olmamasıdır ve aslında hiç bitmeyebilir ve bu nedenle son bir öğeye sahip olmayabilir. (Yani kodunuzun sonsuza kadar çalışacağı anlamına gelir). Öyleyse kalan soru şudur: Neden bir yineleyicinin son öğesini istiyorsunuz?
Lennart Regebro

3
@Peter: Sorunuzu güncelleyin lütfen. Sahip olduğunuz bir soruya çok fazla yorum eklemeyin. Lütfen soruyu güncelleyin ve yorumları kaldırın.
S.Lott

Yanıtlar:


100
item = defaultvalue
for item in my_iter:
    pass

4
Neden yer tutucusu "defaultvalue"? Neden olmasın None? Bu tam olarak ne Noneiçindir. Bazı işleve özgü varsayılan değerlerin doğru olabileceğini mi söylüyorsunuz? Yineleyici gerçekte yinelemiyorsa, bant dışı bir değer, bazı yanıltıcı işleve özgü varsayılan değerlerden daha anlamlıdır.
S.Lott

46
Varsayılan değer, örneğim için yalnızca bir yer tutucudur. NoneVarsayılan değer olarak kullanmak istiyorsanız , bu sizin seçiminizdir. Hiçbiri her zaman en mantıklı varsayılan değildir ve bant dışı bile olmayabilir. Şahsen, gerçekten benzersiz bir değer olduğundan emin olmak için 'defaultvalue = object ()' kullanma eğilimindeyim. Sadece temerrüt seçiminin bu örneğin kapsamı dışında olduğunu belirtiyorum.
Thomas Wouters

28
@ S.Lott: Belki de boş bir yineleyici ile Noneson değeri olan bir yineleyici arasındaki farkı ayırt etmek yararlıdır
John La Rooy

8
Tüm yerleşik konteyner türlerinin tüm yineleyicilerinde bir tasarım hatası mı var? İlk kez duydum :)
Thomas Wouters

7
Bu muhtemelen daha hızlı bir çözüm olsa da, döngülerde sızan değişkene dayanır (bazıları için bir özellik, diğerleri için bir hata - muhtemelen FP adamları dehşete düşmüştür). Her neyse, Guido bunun her zaman bu şekilde çalışacağını, bu nedenle kullanım için güvenli bir yapı olduğunu söyledi.
tokland

68

deque1 beden kullanın .

from collections import deque

#aa is an interator
aa = iter('apple')

dd = deque(aa, maxlen=1)
last_element = dd.pop()

6
Bu aslında uzun bir diziyi tüketmenin en hızlı yoludur, ancak for döngüsünden yalnızca biraz daha hızlıdır.
Sven Marnach

11
Teknik olarak doğru olduğu için +1, ancak okuyucularda "Bunu GERÇEKTEN optimize etmeniz gerekiyor mu?", "Bu daha az açık, Pythonic değil" ve "Daha hızlı hız uygulamaya bağlıdır. değişebilir."
leewz

1
ayrıca, bu bir hafıza
domuzudur

6
@EelcoHoogendoorn Maksimumu 1 olsa bile neden bir hafızayı tüketiyor?
Chris Wesseling

1
Şimdiye kadar burada sunulan tüm çözümlerden, bunu en hızlı ve bellek açısından en verimli olanı buluyorum .
Markus Strauss

67

Python 3.x kullanıyorsanız:

*_, last = iterator # for a better understanding check PEP 448
print(last)

python 2.7 kullanıyorsanız:

last = next(iterator)
for last in iterator:
    continue
print last


Kenar notu:

Genellikle yukarıda sunulan çözüm, normal durumlar için ihtiyacınız olan şeydir, ancak büyük miktarda veriyle uğraşıyorsanız, deque1 boyutunu kullanmak daha etkilidir . ( Kaynak )

from collections import deque

#aa is an interator
aa = iter('apple')

dd = deque(aa, maxlen=1)
last_element = dd.pop()

1
@virtualxtc: Alt çizgi yalnızca bir tanımlayıcıdır. Öndeki yıldız "listeyi genişlet" diyor. Daha okunaklı olacaktır *lst, last = some_iterable.
pepr

4
@virtualxtc nope python'da _özel bir değişkendir ve ya son değeri saklamak ya da değeri umursamadığımı söylemek için kullanılır, böylece temizlenebilir.
DhiaTN

1
Bu Python 3 çözümü bellek açısından verimli değil.
Markus Strauss

3
@DhiaTN Evet, kesinlikle haklısınız. Aslında, çok gösterdiğin Python 3 deyimini beğendim. Sadece şunu açıklığa kavuşturmak istedim, "büyük veri" için çalışmıyor. Bunun için hızlı ve bellek açısından verimli olan collections.deque kullanıyorum (martin23487234'ün çözümüne bakın).
Markus Strauss

1
Bu py3.5 + örneği PEP 448'de olmalıdır. Harika.
EliadL

33

__reversed__Varsa muhtemelen kullanmaya değer

if hasattr(my_iter,'__reversed__'):
    last = next(reversed(my_iter))
else:
    for last in my_iter:
        pass

27

Kadar basit:

max(enumerate(the_iter))[1]

8
Oh, bu akıllıca. En verimli veya okunaklı değil, ama zekice.
timgeb

6
Öyleyse sadece yüksek sesle düşünmek ... Bu işe yarıyor çünkü şöyle enumeratedönüyor (index, value): (0, val0), (1, val1), (2, val2)... ve sonra maxbir demet listesi verildiğinde varsayılan olarak , ilk iki değer eşit olmadıkça, ki hiçbir zaman burada olmadıkları sürece, demetin yalnızca ilk değeriyle karşılaştırır. çünkü endeksleri temsil ediyorlar. Ardından, sondaki alt simge, max'ın tüm (idx, değer) demeti döndürmesidir, oysa biz sadece ilgileniyoruz value. İlginç fikir.
Taylor Edmiston

21

Bu, lambda nedeniyle boş for döngüsünden daha hızlı olması olası değildir, ancak belki başka birine bir fikir verebilir

reduce(lambda x,y:y,my_iter)

Yineleme boşsa, bir TypeError yükseltilir


IMHO, bu kavramsal olarak en doğrudan olanı. TypeErrorBoş bir yinelenebilir için yükseltmek yerine reduce(), örneğin, başlangıç ​​değeri aracılığıyla varsayılan bir değer de sağlayabilirsiniz last = lambda iterable, default=None: reduce(lambda _, x: x, iterable, default).
egnha

9

Bu var

list( the_iter )[-1]

Yinelemenin uzunluğu gerçekten destansı ise - listeyi gerçekleştirmek hafızayı tüketecek kadar uzunsa - o zaman tasarımı gerçekten yeniden düşünmeniz gerekir.


1
Bu en basit çözümdür.
laike9m

2
Bir demet kullanmak biraz daha iyi.
Christopher Smith

9
Son cümleye kesinlikle katılmıyorum. Çok büyük veri kümeleriyle çalışmak (hepsi aynı anda yüklenirse bellek sınırlarını aşabilir), liste yerine yineleyici kullanmanın ana nedenidir.
Paul

@Paul: bazı işlevler yalnızca bir yineleyici döndürür. Bu, bu durumda yapmanın kısa ve oldukça okunaklı bir yoludur (destansı olmayan listeler için).
serv-inc

Kötü, kötü alışkanlıklardan kaçınmanın en az etkili yolu budur. Bir diğeri, dizinin maksimum elemanını elde etmek için sort (sıra) [- 1] kullanmaktır. Yazılım mühendisi olmayı seviyorsanız, lütfen bu kötü kalıpları asla kullanmayın.
Maksym Ganenko

5

reversedSadece yineleyiciler yerine dizileri alması dışında kullanırdım , ki bu oldukça keyfi görünüyor.

Nasıl yaparsan yap, tüm yineleyicinin üzerinden geçmen gerekecek. Maksimum verimlilikte, tekrarlayıcıya bir daha ihtiyacınız yoksa, tüm değerleri çöpe atabilirsiniz:

for last in my_iter:
    pass
# last is now the last item

Yine de bunun optimal olmayan bir çözüm olduğunu düşünüyorum.


4
reversed () bir yineleyici almaz, sadece dizileri alır.
Thomas Wouters

3
Hiç de keyfi değil. Bir yineleyiciyi tersine çevirmenin tek yolu, tüm öğeleri bellekte tutarken sonuna kadar yinelemektir. Ben, e, tersine çevirmeden önce ondan bir sıralama yapmalısın. Elbette ki bu, ilk etapta yineleyicinin amacını bozar ve aynı zamanda görünürde bir neden olmaksızın aniden çok fazla bellek tükettiğiniz anlamına gelir. Yani aslında keyfi olmanın tam tersi. :)
Lennart Regebro

@Lennart - Keyfi dediğimde can sıkıcı demek istedim. Dil becerilerimi, sabahın bu saatinde birkaç saat içinde teslim edilmesi gereken kağıda odaklayacağım.
Chris Lutz

3
Yeterince adil. IMO olmasına rağmen, yinelemeleri kabul etseydi daha can sıkıcı olurdu, çünkü hemen hemen her kullanımı Kötü Fikir (tm) olurdu. :)
Lennart Regebro

3

Toolz kütüphanesi güzel bir çözüm sağlar:

from toolz.itertoolz import last
last(values)

Ancak çekirdek dışı bir bağımlılık eklemek, yalnızca bu durumda kullanmak için buna değmeyebilir.



0

Sadece kullanırdım next(reversed(myiter))


8
TypeError: ters çevrilmiş () argümanı bir sıra olmalıdır
Labo

0

Soru, bir yineleyicinin son öğesini elde etmekle ilgilidir, ancak yineleyiciniz bir diziye koşullar uygulanarak oluşturulmuşsa, tersine çevrilmiş bir dizinin "ilk" ini bulmak için yalnızca gerekli öğelere bakarak, uygulayarak kullanılabilir dizinin kendisine ters.

Yapmacık bir örnek,

>>> seq = list(range(10))
>>> last_even = next(_ for _ in reversed(seq) if _ % 2 == 0)
>>> last_even
8

0

Alternatif olarak sonsuz yineleyiciler için şunları kullanabilirsiniz:

from itertools import islice 
last = list(islice(iterator(), 1000))[-1] # where 1000 is number of samples 

O zaman daha yavaş olacağını düşündüm dequeama bu kadar hızlı ve aslında döngü yönteminden daha hızlı (bir şekilde)


-6

Soru yanlıştır ve yalnızca karmaşık ve verimsiz bir yanıta yol açabilir. Bir yineleyici elde etmek için, elbette yinelenebilir bir şeyden başlarsınız, bu çoğu durumda son öğeye erişmenin daha doğrudan bir yolunu sunar.

Yinelemeden bir yineleyici oluşturduğunuzda, öğelerin içinden geçmek zorunda kalırsınız, çünkü yinelemenin sağladığı tek şey budur.

Dolayısıyla, en verimli ve net yol, ilk etapta yineleyiciyi oluşturmak değil, yinelenebilirin yerel erişim yöntemlerini kullanmaktır.


5
Peki bir dosyanın son satırını nasıl elde edersiniz?
Brice M. Dempsey

@ BriceM.Dempsey En iyi yol, (belki çok büyük) dosyanın tamamını yinelemek değil, eksi 100 dosya boyutuna giderek, son 100 baytı okumak, içlerinde yeni bir satır taramaktır, eğer yoksa, gidin 100 bayt daha geri alın, vb. Senaryonuza bağlı olarak geri adım boyutunuzu da artırabilirsiniz. Gazilyonlarca satır okumak kesinlikle ideal olmayan bir çözümdür.
Alfe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.