Пітонічний спосіб повернення списку кожного n-го елемента у більший список


170

Скажімо, у нас є список чисел від 0 до 1000. Чи існує пітонічний / ефективний спосіб скласти список першого та кожного наступного 10-го елемента, тобто [0, 10, 20, 30, ... ]?

Так, я можу це зробити за допомогою циклу для циклу, але мені цікаво, чи є більш акуратний спосіб зробити це, можливо, навіть в одному рядку?

Відповіді:


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]

Зауважте, що це приблизно в 100 разів швидше, ніж циклічно і перевіряти модуль для кожного елемента:

$ 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
Звичайно, розуміння списку в цілому є більш потужним. Що стосується іншого, то це питання є наявним списком, і, у цьому випадку, фрагмент працює добре.
Нед Дейлі

Я прокоментував це нижче у списку відповідей на розуміння. Будьте обережні з "якщо x% 10 == 0". Він працює лише з цим конкретним прикладом списку, але якщо список вхідних даних є, наприклад, l = діапазон (0,1000,2), він не буде виводити кожен 10-й елемент.
Андре Міллер

12
@Andre: дуже правда. Отже, це приклад функції скромної мови - оператора зрізів, який виявляється в цьому випадку (1) для полегшення отримання правильних результатів; (2) призводить до більш стислого виразу; і (3) швидше на 2 порядки швидше. (1), безумовно, є найважливішим питанням, звичайно, але, завдяки ретельному проектуванню та впровадженню мови, ви отримуєте всіх трьох за ціну 1. Приємного питання та відповіді.
Нед Дейлі

2
У 0надлишку в l[0::10]. l[::10]є більш читабельним, менш заплутаним.
Костянтин Шуберт

Я здивований порівнянням продуктивності 0,5 секунди для розуміння списку та 0,4 секунди для фрагмента списку. Здається, дуже повільно, чому для нарізки списку потрібно 100 тисяч петель для списку розміром 1 тисяча !?
Дамо

57
  1. source_list[::10] є найбільш очевидним, але це не працює для будь-яких ітерабельних і не є ефективною для пам'яті для великих списків.
  2. itertools.islice(source_sequence, 0, None, 10) працює для будь-якого ітерабельного і є ефективною для пам’яті, але, ймовірно, це не найшвидше рішення для великого списку та великого кроку.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))

1
+1 Найкраща відповідь, ІМО. Усі три пропозиції пропонують загальне рішення (тобто прийняти список джерел як даний). Рішення генератора (3.) приємно, оскільки воно фільтрує в індексі списку джерел. Це, мабуть, настільки ж ефективна пам'ять, як і 2. І індекси, і список результатів є генераторами і таким чином ліниво побудовані, що також, мабуть, є найшвидшим, якщо вам не потрібен список результатів в одному шматку. Тільки якщо список джерел міг би бути генератором, я б пішов з ідентифікацією елемента "Пол, i в перерахуванні (l)", оскільки немає генератора (). BTW, який тип ітерабельного не працював би з 1.? Генератори ?!
ThomasH

Ітерабельний = об'єкт методом __iter __ (), що повертає ітератор (об’єкт із методом next ())
Денис Откідач


19

З посібника: 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]


4

Чому б просто не використати параметр кроку функції діапазону , щоб отримати:

l = range(0, 1000, 10)

Для порівняння на моїй машині:

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: Це правда, але оскільки це питання для початківців, функція діапазону може бути тим, що вони насправді хочуть робити, тому я думаю, що це правильна відповідь. (Хоча верхня межа повинна бути 1001, а не 1000)
Скотт Гріффітс

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

1
чому у вас є пункт if, коли діапазон (0, 1001, 10) вже приймає лише кожен 10-й елемент?
Autoplectic

4
Цей же коментар тут, це не вирішує більш загальну проблему "Піфонічного способу повернення списку кожного n-го елемента у більшій список", ваше рішення залежить від того, що значення списку прикладів становлять від 0 до 1000 і лише тягне елементи зі списку, у якого значення, що ділиться на 10, замість кожного 10-го елемента.
Андре Міллер

1
Ну, в ОП пише: "у нас є список чисел від нуля до 1000". Тож у нього немає потреби в загальному рішенні.

1
Він пише "Скажи, що ми маємо ..", що означає його лише приклад. Якби він дійсно хотів, щоб кожне десяте число було зі списку від нуля до 1000, то відповідь буде діапазоном (0,1001,10) або чимось подібним.
Андре Міллер

1

Ось краща реалізація розуміння списку "кожен 10-й елемент", яка не використовує вміст списку як частина тесту на членство:

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

Але це все ще набагато повільніше, ніж просто використання нарізки списку.


-9

Зрозуміння списку зроблено саме для цього:

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

Більш детальну інформацію про них ви можете отримати в офіційній документації python: http://docs.python.org/tutorial/datastructures.html#list-comprehensions


Верхня межа повинна бути 1000, а не 10000. Ваше рішення не включає верхню межу 1000, оскільки діапазон зупиняється на рівні 999. +1 для посилання на розуміння списку.

19
Це насправді не витягує кожен 10-й елемент, він витягує кожен предмет, який має значення, ділене на 10. У цьому конкретному прикладі це те саме, але це може бути не так.
Андре Міллер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.