s = [1,2,3,4,5,6,7,8,9]
n = 3
zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Como zip(*[iter(s)]*n)
funciona? Como seria se fosse escrito com um código mais detalhado?
s = [1,2,3,4,5,6,7,8,9]
n = 3
zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Como zip(*[iter(s)]*n)
funciona? Como seria se fosse escrito com um código mais detalhado?
Respostas:
iter()
é um iterador em uma sequência. [x] * n
produz uma lista contendo a n
quantidade de x
, ou seja, uma lista de comprimento n
, onde cada elemento está x
. *arg
descompacta uma sequência em argumentos para uma chamada de função. Portanto, você está passando o mesmo iterador 3 vezes para zip()
e ele puxa um item do iterador a cada vez.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
yield
s (= return
s) um item, você pode imaginar esse item como "consumido". Portanto, na próxima vez que o iterador for chamado, ele produzirá o próximo item "não consumido".
As outras ótimas respostas e comentários explicam bem as funções de desempacotamento de argumento e zip () .
Como Ignacio e ujukatzel dizem, você passa para zip()
três referências ao mesmo iterador e zip()
faz três tuplas dos inteiros - na ordem - de cada referência ao iterador:
1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9
^ ^ ^
^ ^ ^
^ ^ ^
E já que você pede um exemplo de código mais detalhado:
chunk_size = 3
L = [1,2,3,4,5,6,7,8,9]
# iterate over L in steps of 3
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x
end = start + chunk_size
print L[start:end] # three-item chunks
Seguindo os valores de start
e end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, você pode obter o mesmo resultado map()
com um argumento inicial de None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Para obter mais informações sobre zip()
e map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Acho que uma coisa que faltou em todas as respostas (provavelmente óbvia para aqueles familiarizados com os iteradores), mas não tão óbvia para os outros é -
Como temos o mesmo iterador, ele é consumido e os elementos restantes são usados pelo zip. Portanto, se simplesmente usarmos a lista e não o iter, por exemplo.
l = range(9)
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate
# output
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)]
Usando o iterador, exibe os valores e só permanece disponível, portanto, para zip, uma vez que 0 é consumido, 1 fica disponível e então 2 e assim por diante. Uma coisa muito sutil, mas muito inteligente !!!
iter(s)
retorna um iterador para s.
[iter(s)]*n
faz uma lista de n vezes o mesmo iterador para s.
Portanto, ao fazer zip(*[iter(s)]*n)
isso, ele extrai um item de todos os três iteradores da lista em ordem. Como todos os iteradores são o mesmo objeto, ele apenas agrupa a lista em partes de n
.
Um conselho para usar o zip dessa forma. Isso truncará sua lista se seu comprimento não for uniformemente divisível. Para contornar isso, você pode usar itertools.izip_longest se puder aceitar os valores de preenchimento. Ou você pode usar algo assim:
def n_split(iterable, n):
num_extra = len(iterable) % n
zipped = zip(*[iter(iterable)] * n)
return zipped if not num_extra else zipped + [iterable[-num_extra:], ]
Uso:
for ints in n_split(range(1,12), 3):
print ', '.join([str(i) for i in ints])
Impressões:
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
itertools
receitas: docs.python.org/2/library/itertools.html#recipes grouper
. Não há necessidade de reinventar a roda
Provavelmente é mais fácil ver o que está acontecendo no interpretador python ou ipython
com n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Portanto, temos uma lista de dois iteradores que estão apontando para o mesmo objeto iterador. Lembre-se de que iter
em um objeto retorna um objeto iterador e, neste cenário, é o mesmo iterador duas vezes devido ao *2
açúcar sintático python. Os iteradores também são executados apenas uma vez.
Além disso, zip
leva qualquer número de iteráveis ( sequências são iteráveis ) e cria tupla a partir do i'ésimo elemento de cada uma das sequências de entrada. Como os dois iteradores são idênticos em nosso caso, zip move o mesmo iterador duas vezes para cada tupla de 2 elementos de saída.
In [41]: help(zip)
Help on built-in function zip in module __builtin__:
zip(...)
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
O operador unpacking ( *
) garante que os iteradores sejam executados até a exaustão, que neste caso é até que não haja entrada suficiente para criar uma tupla de 2 elementos.
Isso pode ser estendido a qualquer valor de n
e zip(*[iter(s)]*n)
funciona conforme descrito.
*
é apenas uma conveniência para duplicar um objeto. Experimente com escalares e depois com listas. Pesquisas relacionadas print(*zip(*[iter("ABCDEFG")]*2))
vs print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Em seguida, comece dividindo os dois em etapas menores para ver quais são os objetos iteradores reais nas duas instruções.