Pythonic способ вернуть список каждого n-го элемента в большом списке

171

Скажем , у нас есть список чисел от 0 до 1000. Есть ли вещий / эффективный способ для получения списка из первых и каждого последующего элемента 10, то есть [0, 10, 20, 30, ... ]?

Да, я могу сделать это с помощью цикла for, но мне интересно, есть ли более аккуратный способ сделать это, возможно, даже в одной строке?

p.brown
источник

Ответы:

290
>>> 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
Конечно, списочные представления более сильны в целом. OTOH, вопрос ставит существующий список, и в этом случае срез работает просто отлично.
Нед Дейли
Я прокомментировал это ниже в списке ответов. Будьте осторожны с «если х% 10 == 0». Он работает только с этим конкретным примером списка, но если входной список, например, l = range (0,1000,2), он не будет извлекать каждый 10-й элемент.
Андре Миллер
12
@ Андре: очень верно. Так что это пример скромной языковой функции, оператора среза, который в данном случае (1) облегчает получение правильных результатов; (2) приводит к более краткому выражению; и (3) оказывается на 2 порядка быстрее. (1) безусловно, самая важная проблема, но, благодаря тщательному дизайну и реализации языка, вы получаете все три по цене 1. Хороший вопрос и ответы.
Нед Дейли
2
Это 0избыточно в l[0::10]. l[::10]более читабелен, менее запутан.
Константин Шуберт
Я удивлен сравнением производительности 0,5 секунды для понимания списка и 0,4 секунды для среза списка. Кажется очень медленным, почему для нарезки списка требуется 100 тысяч циклов для списка размером 1 тысяча !?
Дамо
58
  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. И индексы, и список результатов являются генераторами и, следовательно, создаются лениво, что также, вероятно, является самым быстрым, если вам не нужен список результатов в одном фрагменте. Только если список источников мог бы быть генератором, я пошел бы с идиомой Пола "item, i in enumerate (l)", так как нет len () генератора. Кстати, какой тип повторения не будет работать с 1.? Генераторы ?!
ThomasH
Iterable = объект с методом __iter __ (), возвращающий итератор (объект с методом next ())
Денис Откидач
24

Вы можете использовать оператор среза следующим образом:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item
Ник Дандулакис
источник
как получить каждый 2-й предмет, начиная с 3-го?
user1993
4
@ user1993L[2::2]
Ник Дандулакис,
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]
tuergeist
источник
13
newlist = oldlist[::10]

Это выбирает каждый 10-й элемент списка.

Дэвид З
источник
4

Почему бы просто не использовать шаговый параметр функции range, чтобы получить:

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: Это правда, но поскольку это вопрос новичка, функция range может быть тем, чем они действительно хотят заниматься, поэтому я думаю, что это правильный ответ. (Хотя верхний предел должен быть 1001, а не 1000)
Скотт Гриффитс
2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

источник
1
почему у вас есть условие if, когда range (0, 1001, 10) уже принимает только каждый 10-й элемент?
Автоплектика
4
Тот же комментарий здесь, это не решает более общую проблему «Pythonic способ вернуть список каждого n-го элемента в больший список», ваше решение зависит от того факта, что значения списка примера от 0 до 1000 и только вытягивает элементы из списка, который имеет значение, кратное 10 вместо каждого 10-го элемента.
Андре Миллер
1
Ну, ОП пишет: «У нас есть список чисел от нуля до 1000». Поэтому ему не нужно общее решение.
1
Он пишет «Скажи, что у нас есть…», что подразумевает лишь пример. Если бы он действительно хотел, чтобы каждое 10-е число было в списке от нуля до 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']

Но это все еще намного медленнее, чем просто использование списков.

PaulMcG
источник
-9

Для этого созданы списки:

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

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

Santi
источник
Верхняя граница должна быть 1000, а не 10000. Ваше решение не включает в себя верхнюю границу 1000, поскольку диапазон останавливается на 999. +1 для ссылки на понимание списка.
19
Это фактически не вытаскивает каждый 10-й элемент, он вытаскивает каждый элемент, значение которого делится на 10. В этом конкретном примере это то же самое, но это может быть не так.
Андре Миллер