Maneira pitônica de retornar a lista de cada enésimo item de uma lista maior

170

Digamos que tenhamos uma lista de números de 0 a 1000. Existe uma maneira pitônica / eficiente de produzir uma lista do primeiro e de cada décimo item subsequente, ou seja [0, 10, 20, 30, ... ]?

Sim, eu posso fazer isso usando um loop for, mas estou me perguntando se existe uma maneira mais clara de fazer isso, talvez até em uma linha?

p.brown
fonte

Respostas:

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]

Observe que isso é cerca de 100 vezes mais rápido que o loop e a verificação de um módulo para cada elemento:

$ 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
Ned Deily
fonte
4
Claro, a compreensão da lista é mais poderosa em geral. OTOH, a pergunta propõe uma lista existente e, nesse caso, uma fatia funciona perfeitamente.
Ned Deily
Comentei isso abaixo nas respostas de compreensão da lista. Cuidado com "se x% 10 == 0". Ele funciona apenas com este exemplo de lista específico, mas se a lista de entrada for, por exemplo, l = range (0,1000,2), ela não será retirada a cada 10 itens.
11119 Andre Miller
12
@ Andre: muito verdade. Portanto, este é um exemplo de um recurso de linguagem humilde, o operador de fatia, que ocorre neste caso (1) para facilitar a obtenção dos resultados corretos; (2) resulta em uma expressão mais concisa; e (3) passa a ser duas ordens de magnitude mais rápidas. (1) é de longe a preocupação mais importante, é claro, mas, graças ao cuidadoso design e implementação da linguagem, você obtém todos os três pelo preço de 1. Ótimas perguntas e respostas.
Ned Deily
2
O 0é redundante em l[0::10]. l[::10]é mais legível, menos confuso.
Konstantin Schubert
Estou surpreso com a comparação de desempenho: 0,5 segundos para a compreensão da lista e 0,4s para a fatia da lista. Parece muito lento, por que o fatiamento de lista precisa de 100 mil loops para uma lista de tamanho 1 mil !?
Damo
57
  1. source_list[::10] é o mais óbvio, mas isso não funciona para qualquer iterável e não é eficiente em termos de memória para listas grandes.
  2. itertools.islice(source_sequence, 0, None, 10) funciona para qualquer iterável e economiza memória, mas provavelmente não é a solução mais rápida para grandes listas e grandes etapas.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))
Denis Otkidach
fonte
1
+1 Melhor resposta, IMO. Todas as três propostas apresentam soluções gerais (por exemplo, considere a lista de fontes como um dado). A solução do gerador (3.) é boa, pois filtra no índice da lista de fontes. Provavelmente é tão eficiente quanto a memória 2. 2. Os índices e a lista de resultados são geradores e, portanto, construídos preguiçosamente, o que também é provavelmente o mais rápido se você não precisar da lista de resultados em um único bloco. Somente se a lista de fontes pudesse ser um gerador, eu iria com o item "Paul, eu enumerar (l)" o idioma, pois não há len () de um gerador. BTW, que tipo de iterável não funcionaria com 1.? Geradores ?!
ThomasH
Iterable = objecto com __iter __ () método retornando iteração (objeto com método seguinte ())
Denis Otkidach
24

Você pode usar o operador de fatia assim:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item
Nick Dandoulakis
fonte
como obter cada segundo item a partir do terceiro?
user1993
4
@ user1993L[2::2]
Nick Dandoulakis
19

Do manual: 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
fonte
13
newlist = oldlist[::10]

Isso seleciona todos os 10 elementos da lista.

David Z
fonte
4

Por que não usar apenas um parâmetro step da função range para obter:

l = range(0, 1000, 10)

Para comparação, na minha máquina:

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
furgão
fonte
3
@ SilentGhost: Isso é verdade, mas como essa é uma pergunta para iniciantes, a função range pode ser o que eles realmente querem fazer, então acho que é uma resposta válida. (Embora o limite superior deverá ser 1001, não 1000)
Scott Griffiths
2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

fonte
1
por que você tem a cláusula if quando o intervalo (0, 1001, 10) já ocupa apenas cada décimo elemento?
Autoplectic
4
Mesmo comentário aqui, isso não resolve o problema mais geral de "Maneira Pythonic de retornar a lista de todos os itens enésimos em uma lista maior", sua solução depende do fato de que os valores da lista de exemplos são de 0 a 1000 e apenas puxa itens fora da lista que possui um valor divisível por 10 em vez de cada décimo item.
10139 Andre Miller
1
Bem, o OP escreve: "temos uma lista de números de zero a 1000". Portanto, ele não precisa de uma solução geral.
1
Ele escreve 'Diga que temos ...', o que implica apenas um exemplo. Se ele realmente quisesse cada décimo número de uma lista de zero a 1000, a resposta seria um intervalo (0,1001,10) ou algo semelhante.
Andre Miller
1

Aqui está uma melhor implementação de uma compreensão da lista de "cada 10 itens", que não usa o conteúdo da lista como parte do teste de associação:

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

Mas isso ainda é muito mais lento do que apenas usar o fatiamento de lista.

PaulMcG
fonte
-9

As compreensões de lista são feitas exatamente para isso:

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

Você pode obter mais informações sobre eles na documentação oficial do python: http://docs.python.org/tutorial/datastructures.html#list-comprehensions

Santi
fonte
O limite superior deve ser 1000, e não 10000. Sua solução não inclui o limite superior de 1000, pois o intervalo para 999. +1 para o link para a compreensão da lista.
19
Na verdade, isso não extrai cada décimo item, extrai cada item que tem um valor divisível por 10. Nesse exemplo em particular, é a mesma coisa, mas pode não ser.
10139 Andre Miller