Pytanie Najczystszy sposób na pobranie ostatniego elementu z iteratora Pythona


Jaki jest najlepszy sposób na uzyskanie ostatniego elementu z iteratora w Pythonie 2.6? Powiedz na przykład

my_iter = iter(range(5))

Jaki jest najkrótszy kod / najczystszy sposób na uzyskanie 4 od my_iter?

Mógłbym to zrobić, ale nie wydaje się to bardzo skuteczne:

[x for x in my_iter][-1]

76
2018-01-26 10:53


pochodzenie


Iteratory zakładają, że chcesz dokonać iteracji poprzez elementy i nie uzyskać dostępu do ostatnich elementów. Co powstrzymuje cię od używania zakresu (5) [- 1]? - Frank
@Frank - założyłem, że rzeczywisty iterator był bardziej złożony i / lub dalej i / lub trudniejszy do kontrolowania niż iter(range(5)) - Chris Lutz
@Frank: fakt, że jest to znacznie bardziej skomplikowana funkcja generatora, która dostarcza iterator. Właśnie zrobiłem ten przykład, aby było jasne i proste, co się dzieje. - Peter
Jeśli chcesz mieć ostatni element iteratora, istnieje duża szansa, że ​​robisz coś nie tak. Ale odpowiedź brzmi, że nie ma żadnego czystszego sposobu na iterację przez iterator. Dzieje się tak, ponieważ iteratory nie mają rozmiaru, a w rzeczywistości mogą nigdy nie kończyć się i jako takie mogą nie mieć ostatniego elementu. (To znaczy, że twój kod będzie działać wiecznie, oczywiście). Pozostaje więc pytanie: dlaczego chcesz ostatni element iteratora? - Lennart Regebro
@Peter: Proszę zaktualizować pytanie. Nie dodawaj garści komentarzy do swoich własnych pytań. Zaktualizuj pytanie i usuń komentarze. - S.Lott


Odpowiedzi:


item = defaultvalue
for item in my_iter:
    pass

70
2018-01-26 10:56



Dlaczego symbol zastępczy "wartość domyślna"? Dlaczego nie None? Dokładnie to None jest dla. Sugerujesz, że niektóre wartości domyślne dla danej funkcji mogą być poprawne? Jeśli iterator w rzeczywistości nie wykonuje iteracji, to wartość poza pasmem jest jeszcze znaczące niż niektóre wprowadzające w błąd domyślne funkcje. - S.Lott
Wartość domyślna jest po prostu symbolem zastępczym dla mojego przykładu. Jeśli chcesz użyć None jako wartość domyślna, to twój wybór. Żadna nie zawsze jest najbardziej sensowną wartością domyślną i może nawet nie być poza zespołem. Osobiście staram się używać "defaultvalue = object ()", aby upewnić się, że jest to naprawdę unikalna wartość. Po prostu wskazuję, że wybór domyślny wykracza poza zakres tego przykładu. - Thomas Wouters
@ S.Lott: być może warto rozróżnić różnicę między pustym iteratorem a iteratorem, który ma None ponieważ jest to wartość końcowa - John La Rooy
Wystąpił błąd projektowy we wszystkich iteratorach wszystkich wbudowanych typów kontenerów? Pierwszy raz o tym słyszałem :) - Thomas Wouters
Chociaż jest to prawdopodobnie najszybsze rozwiązanie, polega na zmiennej wycieku w pętlach for (funkcja dla niektórych, błąd dla innych - prawdopodobnie faceci z FP są przerażeni). W każdym razie, Guido powiedział, że to zawsze zadziała w ten sposób, więc jest bezpieczna w użyciu. - tokland


Użyć deque o wielkości 1.

from collections import deque

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

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

41
2017-07-02 23:39



Jest to w rzeczywistości najszybszy sposób na wyczerpanie długiej sekwencji, ale tylko nieco szybszej niż pętla for. - Sven Marnach
+1 za poprawność techniczną, ale czytelnicy powinni mieć zwyczajowe zastrzeżenia Pythona: "Czy NAPRAWDĘ potrzebujesz tego zoptymalizować?", "To jest mniej jednoznaczne, co nie jest Pythoniczne" i "Szybsza prędkość zależy od implementacji, która może zmienić." - leewz
także jest to wspomnienie-świst - Eelco Hoogendoorn
@EcocoHoogendoorn Dlaczego to jest pamięć-wieniec, nawet z maksimum 1? - Chris Wesseling


Prawdopodobnie warto go użyć __reversed__ jeśli jest dostępny

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

29
2018-02-05 23:01





Jeśli używasz Pythona 3.x:

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

jeśli używasz Pythona 2.7:

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

22
2018-01-12 19:07



Czy mógłbyś wyjaśnić to *_ część? - virtualxtc
Sprawdzanie @virtualxtc PEP 448 po więcej szczegółów - DhiaTN
@virtualxtc: Podkreślenie to tylko identyfikator. Gwiazda z przodu mówi "rozwiń listę". Bardziej czytelny byłby *lst, last = some_iterable. - pepr
@virtualxtc nope _ jest specjalną zmienną w pythonie i służy do przechowywania ostatniej wartości lub do powiedzenia, że ​​nie zależy mi na wartości, więc można ją wyczyścić. - DhiaTN


Jest mało prawdopodobne, aby był szybszy od pustej pętli for z powodu lambda, ale może da to komuś pomysł

reduce(lambda x,y:y,my_iter)

Jeśli iter jest pusty, wywoływany jest TypeError


18
2018-01-26 11:59





Tak prosty jak:

max(enumerate(the_iter))[1]

16
2018-06-06 11:24



Och, to jest sprytne. Nie najskuteczniejszy ani czytelny, ale sprytny. - timgeb
Więc po prostu myśl na głos ... To działa, ponieważ enumerate zwraca (index, value) lubić: (0, val0), (1, val1), (2, val2)... a następnie domyślnie max gdy podano listę krotek, porównuje się tylko z pierwszą wartością krotki, chyba że dwie pierwsze wartości są równe, które nigdy nie są tutaj, ponieważ reprezentują indeksy. Następnie dolny znak dolny jest, ponieważ max zwraca całą krotkę (idx, wartość), podczas gdy my jesteśmy tylko zainteresowani value. Ciekawy pomysł. - Taylor Edmiston


To jest to

list( the_iter )[-1]

Jeśli długość iteracji jest naprawdę epicka - tak długo, że zmaterializowanie listy spowoduje wyczerpanie pamięci - wtedy naprawdę trzeba ponownie przemyśleć projekt.


4
2018-01-26 11:42



To najprostsze rozwiązanie. - laike9m
Łagodnie lepiej użyć krotki. - Christopher Smith
Zdecydowanie nie zgadzam się z ostatnim zdaniem. Praca z bardzo dużymi zbiorami danych (które mogą przekroczyć limity pamięci, jeśli są załadowane wszystkie naraz) jest głównym powodem używania iteratora zamiast listy. - Paul
@Paul: niektóre funkcje zwracają tylko iteratory. Jest to krótki i całkiem czytelny sposób na zrobienie tego w tym przypadku (dla list nie-epickich). - serv-inc


użyłbym reversed, z tym że bierze tylko sekwencje zamiast iteratorów, co wydaje się raczej arbitralne.

Jakikolwiek sposób to zrobisz, będziesz musiał przejść przez cały iterator. Przy maksymalnej wydajności, jeśli nie potrzebujesz już iteratora, możesz po prostu wyrzucić wszystkie wartości:

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

Myślę, że to jednak nieoptymalne rozwiązanie.


2
2018-01-26 10:57



reverse () nie przyjmuje iteratora, tylko sekwencje. - Thomas Wouters
To wcale nie jest arbitralne. Jedynym sposobem na odwrócenie iteratora jest iteracja do końca, przy zachowaniu wszystkich elementów w pamięci. Ja, e, musisz najpierw zrobić z tego sekwencję, zanim będziesz mógł ją odwrócić. Co oczywiście pogarsza cel iteratora, a także oznacza, że ​​nagle zużywasz dużo pamięci bez wyraźnego powodu. W rzeczywistości jest to przeciwieństwo arbitralności. :) - Lennart Regebro
@Lennart - Kiedy powiedziałem, że jestem arbitralny, miałem na myśli irytujące. Skupiam swoje umiejętności językowe na mojej gazecie w ciągu kilku godzin o tej porze. - Chris Lutz
Słusznie. Chociaż IMO byłoby bardziej irytujące, gdyby przyjmowało iteratory, ponieważ prawie każde jego użycie byłoby złym pomysłem (tm). :) - Lennart Regebro


Zobacz ten kod dla czegoś podobnego:

http://excamera.com/sphinx/article-islast.html

możesz go użyć do odebrania ostatniego przedmiotu za pomocą:

[(last, e) for (last, e) in islast(the_iter) if last]

1
2018-01-12 18:08



Proszę podać kod dla islast w twojej odpowiedzi (zob meta.stackexchange.com/questions/8231/...). - Cristian Ciupitu


Po prostu skorzystam next(reversed(myiter))


0
2017-10-24 18:48



TypeError: argument to reversed () musi być sekwencją - Labo