Na marginesie stwierdziłem, że list()funkcja będzie iterować przez swój argument (iterowalny). W ten sposób wywołując list()dwukrotnie tę samą iterowalną opcję (np. Wynik zip()), przy drugim wywołaniu otrzymasz pustą listę!
theaws.blog
Odpowiedzi:
84
Widzę wiele odpowiedzi sugerujących itertools.tee , ale to ignoruje jedno kluczowe ostrzeżenie w dokumentacji:
To narzędzie itertool może wymagać znacznej ilości pamięci dyskowej (w zależności od tego, ile danych tymczasowych należy przechowywać). Ogólnie rzecz biorąc, jeśli jeden iterator używa większości lub wszystkich danych przed uruchomieniem innego iteratora, jest szybszy w użyciu list()zamiast tee().
Zasadniczo teejest przeznaczony do sytuacji, w których dwa (lub więcej) klony jednego iteratora, „tracąc synchronizację” ze sobą, nie robią tego zbytnio - raczej mówią w tym samym „sąsiedztwie” (a kilka elementów za lub przed sobą). Nie nadaje się do problemu „ponów od początku” w PO.
L = list(DictReader(...))z drugiej strony jest idealny, o ile lista dyktandów mieści się wygodnie w pamięci. Nowy „iterator od samego początku” (bardzo lekki i niewielki) może być utworzony w dowolnym momencie iter(L)i używany w części lub w całości bez wpływu na nowe lub istniejące; łatwo dostępne są również inne wzorce dostępu.
Jak słusznie zauważono w kilku odpowiedziach, w konkretnym przypadku csvmożesz również podać .seek(0)podstawowy obiekt pliku (raczej szczególny przypadek). Nie jestem pewien, czy jest to udokumentowane i gwarantowane, chociaż obecnie działa; prawdopodobnie warto byłoby rozważyć to tylko dla naprawdę dużych plików csv, w których listpolecam, ponieważ podejście ogólne miałoby zbyt duży ślad pamięci.
Wtedy będziesz mógł pobrać następną linię reader.next(), która powinna zostać wyświetlona
{'a':1,'b':2,'c':3,'d':4}
ponowne użycie go przyniesie
{'a':2,'b':3,'c':4,'d':5}
Jednak w tym momencie, jeśli użyjesz blah.seek(0), następnym razem, gdy zadzwonisz reader.next(), otrzymasz
{'a':1,'b':2,'c':3,'d':4}
jeszcze raz.
Wydaje się, że jest to funkcja, której szukasz. Jestem pewien, że są pewne sztuczki związane z tym podejściem, których jednak nie jestem świadomy. @Brian zasugerował po prostu utworzenie kolejnego DictReadera. To nie zadziała, jeśli pierwszy czytelnik jest w połowie czytania pliku, ponieważ nowy czytnik będzie miał nieoczekiwane klucze i wartości z dowolnego miejsca w pliku.
Tak powiedziała mi moja teoria, miło widzieć, że to, co myślałem, powinno się wydarzyć.
Wayne Werner,
@Wilduck: zachowanie, które opisujesz za pomocą innej instancji DictReader, nie nastąpi, jeśli utworzysz nowy uchwyt pliku i przekażesz go do drugiego DictReader, prawda?
Jeśli masz dwa programy obsługi plików, będą one działać niezależnie, tak.
Wilduck
24
Nie. Protokół iteratora w Pythonie jest bardzo prosty i zapewnia tylko jedną metodę ( .next()lub __next__()) i nie ma żadnej metody resetowania iteratora w ogóle.
Typowym wzorcem jest zamiast tego utworzenie nowego iteratora przy użyciu tej samej procedury ponownie.
Jeśli chcesz "zapisać" iterator, aby móc wrócić do jego początku, możesz również rozwidlić iterator za pomocą itertools.tee
Chociaż analiza metody .next () jest prawdopodobnie poprawna, istnieje dość prosty sposób na uzyskanie tego, o co prosi operacja.
Wilduck,
2
@Wilduck: Widzę, że twoja odpowiedź. Właśnie odpowiedziałem na pytanie iteratora i nie mam pojęcia o csvmodule. Miejmy nadzieję, że obie odpowiedzi będą przydatne w oryginalnym plakacie.
u0b34a0f6ae
Ściśle rzecz biorąc, protokół iteratora również wymaga __iter__. Oznacza to, że iteratory również muszą być iterowalne.
Steve Jessop
11
Tak , jeśli używasz numpy.nditerdo budowania swojego iteratora.
Jest błąd w używaniu, .seek(0)jak zalecali Alex Martelli i Wilduck powyżej, a mianowicie, że następne wywołanie .next()da ci słownik twojego wiersza nagłówka w postaci {key1:key1, key2:key2, ...}. Obejście polega na file.seek(0)wywołaniu polecenia, reader.next()aby pozbyć się wiersza nagłówka.
Twój kod wyglądałby więc mniej więcej tak:
f_in = open('myfile.csv','r')
reader = csv.DictReader(f_in)for record in reader:if some_condition:# reset reader to first row of data on 2nd line of file
f_in.seek(0)
reader.next()continue
do_something(record)
Jest to prawdopodobnie prostopadłe do pierwotnego pytania, ale można by opakować iterator w funkcję, która zwraca iterator.
def get_iter():return iterator
Aby zresetować iterator, po prostu ponownie wywołaj funkcję. Jest to oczywiście trywialne, jeśli funkcja, gdy wspomniana funkcja nie przyjmuje argumentów.
W przypadku, gdy funkcja wymaga pewnych argumentów, użyj functools.partial, aby utworzyć zamknięcie, które można przekazać zamiast oryginalnego iteratora.
Chociaż nie ma resetowania iteratora, moduł „itertools” z Pythona 2.6 (i nowszych) ma kilka narzędzi, które mogą w tym pomóc. Jednym z nich jest „tee”, który może tworzyć wiele kopii iteratora i buforować wyniki kolejnego, tak aby były one używane na kopiach. Zepsuję twoje cele:
>>>def printiter(n):...for i in xrange(n):...print"iterating value %d"% i
...yield i
>>>from itertools import tee
>>> a, b = tee(printiter(5),2)>>> list(a)
iterating value 0
iterating value 1
iterating value 2
iterating value 3
iterating value 4[0,1,2,3,4]>>> list(b)[0,1,2,3,4]
Miałem wcześniej ten sam problem. Po przeanalizowaniu mojego kodu zdałem sobie sprawę, że próba zresetowania iteratora wewnątrz pętli nieznacznie zwiększa złożoność czasową, a także sprawia, że kod jest nieco brzydki.
Rozwiązanie
Otwórz plik i zapisz wiersze w zmiennej w pamięci.
# initialize list of rows
rows =[]# open the file and temporarily name it as 'my_file'with open('myfile.csv','rb')as my_file:# set up the reader using the opened file
myfilereader = csv.DictReader(my_file)# loop through each row of the readerfor row in myfilereader:# add the row to the list of rows
rows.append(row)
Teraz możesz zapętlać wiersze w dowolnym miejscu zakresu bez korzystania z iteratora.
Dochodzę do tego samego problemu - chociaż podoba mi się to tee()rozwiązanie, nie wiem, jak duże będą moje pliki, a ostrzeżenia dotyczące pamięci o zużyciu jednego przed drugim zniechęcają mnie do przyjęcia tej metody.
Zamiast tego tworzę parę iteratorów za pomocą iter()instrukcji i używam pierwszego do mojego początkowego uruchomienia, przed przełączeniem się na drugi dla końcowego przebiegu.
Tak więc w przypadku czytnika dyktafonu, jeśli czytelnik jest zdefiniowany za pomocą:
d = csv.DictReader(f, delimiter=",")
Mogę utworzyć parę iteratorów na podstawie tej „specyfikacji” - używając:
d1, d2 = iter(d), iter(d)
Mogę wtedy uruchomić kod pierwszego przejścia d1, mając pewność, że drugi iterator d2został zdefiniowany na podstawie tej samej specyfikacji głównej.
Nie testowałem tego do końca, ale wydaje się, że działa z fikcyjnymi danymi.
list()
funkcja będzie iterować przez swój argument (iterowalny). W ten sposób wywołująclist()
dwukrotnie tę samą iterowalną opcję (np. Wynikzip()
), przy drugim wywołaniu otrzymasz pustą listę!Odpowiedzi:
Widzę wiele odpowiedzi sugerujących itertools.tee , ale to ignoruje jedno kluczowe ostrzeżenie w dokumentacji:
Zasadniczo
tee
jest przeznaczony do sytuacji, w których dwa (lub więcej) klony jednego iteratora, „tracąc synchronizację” ze sobą, nie robią tego zbytnio - raczej mówią w tym samym „sąsiedztwie” (a kilka elementów za lub przed sobą). Nie nadaje się do problemu „ponów od początku” w PO.L = list(DictReader(...))
z drugiej strony jest idealny, o ile lista dyktandów mieści się wygodnie w pamięci. Nowy „iterator od samego początku” (bardzo lekki i niewielki) może być utworzony w dowolnym momencieiter(L)
i używany w części lub w całości bez wpływu na nowe lub istniejące; łatwo dostępne są również inne wzorce dostępu.Jak słusznie zauważono w kilku odpowiedziach, w konkretnym przypadku
csv
możesz również podać.seek(0)
podstawowy obiekt pliku (raczej szczególny przypadek). Nie jestem pewien, czy jest to udokumentowane i gwarantowane, chociaż obecnie działa; prawdopodobnie warto byłoby rozważyć to tylko dla naprawdę dużych plików csv, w którychlist
polecam, ponieważ podejście ogólne miałoby zbyt duży ślad pamięci.źródło
list()
do buforowania wielu przejść przez csvreader w pliku o wielkości 5 MB, mój czas działania skraca się z ~ 12 sekund do ~ 0,5 sekundy.Jeśli masz plik csv o nazwie „blah.csv”, to wygląda tak
wiesz, że możesz otworzyć plik do czytania i utworzyć DictReader za pomocą
Wtedy będziesz mógł pobrać następną linię
reader.next()
, która powinna zostać wyświetlonaponowne użycie go przyniesie
Jednak w tym momencie, jeśli użyjesz
blah.seek(0)
, następnym razem, gdy zadzwoniszreader.next()
, otrzymaszjeszcze raz.
Wydaje się, że jest to funkcja, której szukasz. Jestem pewien, że są pewne sztuczki związane z tym podejściem, których jednak nie jestem świadomy. @Brian zasugerował po prostu utworzenie kolejnego DictReadera. To nie zadziała, jeśli pierwszy czytelnik jest w połowie czytania pliku, ponieważ nowy czytnik będzie miał nieoczekiwane klucze i wartości z dowolnego miejsca w pliku.
źródło
Nie. Protokół iteratora w Pythonie jest bardzo prosty i zapewnia tylko jedną metodę (
.next()
lub__next__()
) i nie ma żadnej metody resetowania iteratora w ogóle.Typowym wzorcem jest zamiast tego utworzenie nowego iteratora przy użyciu tej samej procedury ponownie.
Jeśli chcesz "zapisać" iterator, aby móc wrócić do jego początku, możesz również rozwidlić iterator za pomocą
itertools.tee
źródło
csv
module. Miejmy nadzieję, że obie odpowiedzi będą przydatne w oryginalnym plakacie.__iter__
. Oznacza to, że iteratory również muszą być iterowalne.Tak , jeśli używasz
numpy.nditer
do budowania swojego iteratora.źródło
nditer
przechodzić przez tablicęitertools.cycle
?try:
next()
StopIteration
reset()
next()
Jest błąd w używaniu,
.seek(0)
jak zalecali Alex Martelli i Wilduck powyżej, a mianowicie, że następne wywołanie.next()
da ci słownik twojego wiersza nagłówka w postaci{key1:key1, key2:key2, ...}
. Obejście polega nafile.seek(0)
wywołaniu polecenia,reader.next()
aby pozbyć się wiersza nagłówka.Twój kod wyglądałby więc mniej więcej tak:
źródło
Jest to prawdopodobnie prostopadłe do pierwotnego pytania, ale można by opakować iterator w funkcję, która zwraca iterator.
Aby zresetować iterator, po prostu ponownie wywołaj funkcję. Jest to oczywiście trywialne, jeśli funkcja, gdy wspomniana funkcja nie przyjmuje argumentów.
W przypadku, gdy funkcja wymaga pewnych argumentów, użyj functools.partial, aby utworzyć zamknięcie, które można przekazać zamiast oryginalnego iteratora.
Wydaje się, że pozwala to uniknąć buforowania, które musiałyby zrobić tee (n kopii) lub list (1 kopia)
źródło
W przypadku małych plików możesz rozważyć użycie
more_itertools.seekable
- narzędzia innej firmy, które oferuje resetowanie iterowalnych.Próbny
Wynik
Tutaj a
DictReader
jest zawinięty wseekable
obiekt (1) i zaawansowany (2).seek()
Sposób służy do zerowania / tyłu iteracyjnej do położenia 0th (3).Uwaga: zużycie pamięci rośnie wraz z iteracją, więc zachowaj ostrożność, stosując to narzędzie do dużych plików, jak wskazano w dokumentacji .
źródło
Chociaż nie ma resetowania iteratora, moduł „itertools” z Pythona 2.6 (i nowszych) ma kilka narzędzi, które mogą w tym pomóc. Jednym z nich jest „tee”, który może tworzyć wiele kopii iteratora i buforować wyniki kolejnego, tak aby były one używane na kopiach. Zepsuję twoje cele:
źródło
W przypadku DictReader:
W przypadku DictWriter:
źródło
list(generator())
zwraca wszystkie pozostałe wartości dla generatora i skutecznie resetuje go, jeśli nie jest zapętlony.źródło
Problem
Miałem wcześniej ten sam problem. Po przeanalizowaniu mojego kodu zdałem sobie sprawę, że próba zresetowania iteratora wewnątrz pętli nieznacznie zwiększa złożoność czasową, a także sprawia, że kod jest nieco brzydki.
Rozwiązanie
Otwórz plik i zapisz wiersze w zmiennej w pamięci.
Teraz możesz zapętlać wiersze w dowolnym miejscu zakresu bez korzystania z iteratora.
źródło
Jedną z możliwych opcji jest użycie
itertools.cycle()
, które pozwoli ci iterować w nieskończoność bez żadnych sztuczek, takich jak.seek(0)
.źródło
Dochodzę do tego samego problemu - chociaż podoba mi się to
tee()
rozwiązanie, nie wiem, jak duże będą moje pliki, a ostrzeżenia dotyczące pamięci o zużyciu jednego przed drugim zniechęcają mnie do przyjęcia tej metody.Zamiast tego tworzę parę iteratorów za pomocą
iter()
instrukcji i używam pierwszego do mojego początkowego uruchomienia, przed przełączeniem się na drugi dla końcowego przebiegu.Tak więc w przypadku czytnika dyktafonu, jeśli czytelnik jest zdefiniowany za pomocą:
Mogę utworzyć parę iteratorów na podstawie tej „specyfikacji” - używając:
Mogę wtedy uruchomić kod pierwszego przejścia
d1
, mając pewność, że drugi iteratord2
został zdefiniowany na podstawie tej samej specyfikacji głównej.Nie testowałem tego do końca, ale wydaje się, że działa z fikcyjnymi danymi.
źródło
Zwraca nowo utworzony iterator w ostatniej iteracji podczas wywołania „iter ()”
Wynik:
źródło
Tylko wtedy, gdy typ bazowy zapewnia mechanizm umożliwiający to (np
fp.seek(0)
.).źródło