Pythonowy sposób na zwrócenie listy każdej n-tej pozycji na większej liście


170

Powiedzmy, że mamy listę liczb od 0 do 1000. Czy istnieje pythonowy / wydajny sposób na utworzenie listy pierwszej i każdej następnej dziesiątej pozycji, tj. [0, 10, 20, 30, ... ]?

Tak, mogę to zrobić za pomocą pętli for, ale zastanawiam się, czy istnieje lepszy sposób, aby to zrobić, być może nawet w jednej linii?

Odpowiedzi:


289
>>> lst = list(range(165))
>>> lst[0::10]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]

Zauważ, że jest to około 100 razy szybsze niż zapętlanie i sprawdzanie modułu dla każdego elementu:

$ python -m timeit -s "lst = list(range(1000))" "lst1 = [x for x in lst if x % 10 == 0]"
1000 loops, best of 3: 525 usec per loop
$ python -m timeit -s "lst = list(range(1000))" "lst1 = lst[0::10]"
100000 loops, best of 3: 4.02 usec per loop

4
Jasne, listy składane są generalnie silniejsze. OTOH, pytanie zakłada istniejącą listę iw tym przypadku wycinek działa dobrze.
Ned Deily,

Skomentowałem to poniżej w liście odpowiedzi ze zrozumieniem. Uważaj na "if x% 10 == 0". Działa tylko z tym konkretnym przykładem listy, ale jeśli lista wejściowa jest na przykład l = zakres (0,1000,2), nie będzie wyciągać co 10 pozycji.
Andre Miller,

12
@Andre: bardzo prawdziwe. To jest przykład skromnej funkcji językowej, operatora plasterka, który okazuje się w tym przypadku (1), aby ułatwić uzyskanie poprawnych wyników; (2) daje bardziej zwięzłe sformułowanie; i (3) jest o 2 rzędy wielkości szybsze. (1) jest oczywiście najważniejszym problemem, ale dzięki starannemu projektowi i implementacji języka otrzymujesz wszystkie trzy za cenę 1. Ładne pytanie i odpowiedzi.
Ned Deily,

2
0Jest zbędna w l[0::10]. l[::10]jest bardziej czytelny, mniej zagmatwany.
Konstantin Schubert,

Jestem zaskoczony porównaniem wydajności 0,5 sekundy dla zrozumienia listy i 0,4 sekundy dla wycinka listy. Wydaje się bardzo powolne, dlaczego cięcie listy wymaga 100 tysięcy pętli dla listy o rozmiarze 1 tysiąca !?
Damo

57
  1. source_list[::10] jest najbardziej oczywiste, ale nie działa to dla żadnej iterowalnej i nie zapewnia wydajnej pamięci w przypadku dużych list.
  2. itertools.islice(source_sequence, 0, None, 10) działa dla dowolnej iterowalnej i wydajnej pamięci, ale prawdopodobnie nie jest najszybszym rozwiązaniem w przypadku dużej listy i dużego kroku.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))

1
+1 Najlepsza odpowiedź, IMO. Wszystkie trzy propozycje są rozwiązaniami ogólnymi (tj. Przyjmują listę źródeł jako dane). Rozwiązanie generatora (3.) jest fajne, ponieważ filtruje indeks listy źródeł. Prawdopodobnie jest tak wydajny pod względem pamięci, jak 2. Zarówno indeksy, jak i lista wyników są generatorami, a zatem są konstruowane leniwie, co jest również prawdopodobnie najszybsze, jeśli nie potrzebujesz listy wyników w jednym kawałku. Tylko jeśli lista źródeł mogłaby być generatorem, użyłbym idiomu Paula „item, i w enumerate (l)”, ponieważ nie ma len () generatora. BTW, który rodzaj iteracji nie zadziała z 1.? Generatory ?!
ThomasH

Iterable = obiekt z metodą __iter __ () zwracającą iterator (obiekt z metodą next ())
Denis Otkidach


19

Z instrukcji: s[i:j:k] slice of s from i to j with step k

li = range(100)
sub = li[0::10]

>>> sub
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

13
newlist = oldlist[::10]

To wybiera co dziesiąty element listy.


4

Dlaczego nie użyć parametru kroku funkcji zakresu, aby uzyskać:

l = range(0, 1000, 10)

Dla porównania na moim komputerze:

H:\>python -m timeit -s "l = range(1000)" "l1 = [x for x in l if x % 10 == 0]"
10000 loops, best of 3: 90.8 usec per loop
H:\>python -m timeit -s "l = range(1000)" "l1 = l[0::10]"
1000000 loops, best of 3: 0.861 usec per loop
H:\>python -m timeit -s "l = range(0, 1000, 10)"
100000000 loops, best of 3: 0.0172 usec per loop

3
@SilentGhost: To prawda, ale ponieważ jest to pytanie dla początkujących, funkcja zakresu może być tym, co naprawdę chcą zrobić, więc myślę, że jest to poprawna odpowiedź. (Chociaż górna granica powinna wynosić 1001, a nie 1000)
Scott Griffiths

2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

1
dlaczego masz klauzulę if, skoro zakres (0, 1001, 10) zajmuje już tylko co dziesiąty element?
Autoplectic

4
Ten sam komentarz tutaj, to nie rozwiązuje bardziej ogólnego problemu "Pythonowego sposobu zwracania listy co n-tej pozycji na większej liście" Twoje rozwiązanie zależy od faktu że przykładowa lista ma wartości od 0 do 1000 i pobiera tylko pozycje z listy, która ma wartość podzielną przez 10 zamiast co dziesiątej pozycji.
Andre Miller,

1
Otóż ​​OP pisze: „mamy listę liczb od zera do 1000”. Więc nie potrzebuje ogólnego rozwiązania.

1
Pisze „Powiedz, że mamy…”, co oznacza, że ​​jest to tylko przykład. Gdyby naprawdę chciał, aby co dziesiąta liczba z listy od zera do 1000, to odpowiedzią byłby zakres (0,1001,10) lub coś podobnego.
Andre Miller

1

Oto lepsza implementacja rozumienia listy „co 10 pozycja”, która nie wykorzystuje zawartości listy jako części testu członkostwa:

>>> l = range(165)
>>> [ item for i,item in enumerate(l) if i%10==0 ]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
>>> l = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
>>> [ item for i,item in enumerate(l) if i%10==0 ]
['A', 'K', 'U']

Ale jest to nadal znacznie wolniejsze niż zwykłe wycinanie list.


-9

Listy składane są dokładnie do tego przeznaczone:

smaller_list = [x for x in range(100001) if x % 10 == 0]

Możesz uzyskać więcej informacji na ich temat w oficjalnej dokumentacji Pythona: http://docs.python.org/tutorial/datastructures.html#list-comprehensions


Górna granica powinna wynosić 1000, a nie 10000. Twoje rozwiązanie nie obejmuje górnej granicy 1000, ponieważ zakres kończy się na 999. +1 dla łącza do rozumienia listy.

19
To właściwie nie wyciąga co dziesiątego elementu, ale wyciąga każdy element, który ma wartość podzielną przez 10. W tym konkretnym przykładzie to to samo, ale może nie być.
Andre Miller,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.