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


171

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

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

Ответы:


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 ())
Денис Откидач


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

Почему бы просто не использовать шаговый параметр функции 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']

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


-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.