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)]
Bagaimana cara zip(*[iter(s)]*n)
kerjanya? Seperti apa jadinya jika ditulis dengan lebih banyak kode verbose?
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)]
Bagaimana cara zip(*[iter(s)]*n)
kerjanya? Seperti apa jadinya jika ditulis dengan lebih banyak kode verbose?
Jawaban:
iter()
adalah iterator yang berurutan. [x] * n
menghasilkan daftar yang berisi n
kuantitas x
, yaitu daftar panjang n
, di mana setiap elemen berada x
. *arg
membongkar urutan menjadi argumen untuk panggilan fungsi. Oleh karena itu, Anda meneruskan iterator yang sama 3 kali ke zip()
, dan itu menarik item dari iterator setiap saat.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
yield
s (= return
s) item, Anda dapat membayangkan item ini sebagai "dikonsumsi". Jadi pada saat iterator dipanggil, itu menghasilkan item "tidak dikonsumsi" berikutnya.
Jawaban dan komentar hebat lainnya menjelaskan dengan baik peran argument unpacking dan zip () .
Seperti yang dikatakan Ignacio dan ujukatzel , Anda meneruskan ke zip()
tiga referensi ke iterator yang sama dan zip()
membuat 3-tupel dari bilangan bulat — secara berurutan — dari setiap referensi ke iterator:
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
^ ^ ^
^ ^ ^
^ ^ ^
Dan karena Anda meminta contoh kode yang lebih panjang:
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
Mengikuti nilai start
dan end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, Anda bisa mendapatkan hasil yang sama dengan map()
argumen awal None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Untuk lebih lanjut tentang zip()
dan map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Saya pikir satu hal yang terlewat di semua jawaban (mungkin jelas bagi mereka yang akrab dengan iterator) tetapi tidak begitu jelas bagi orang lain adalah -
Karena kita memiliki iterator yang sama, itu dikonsumsi dan elemen yang tersisa digunakan oleh zip. Jadi jika kita hanya menggunakan daftar dan bukan iter misalnya.
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)]
Dengan menggunakan iterator, munculkan nilai dan hanya tetap tersedia, jadi untuk zip setelah 0 dikonsumsi 1 tersedia dan kemudian 2 dan seterusnya. Hal yang sangat halus, tapi cukup pintar !!!
iter(s)
mengembalikan iterator untuk s.
[iter(s)]*n
membuat daftar n kali iterator yang sama untuk s.
Jadi, saat melakukannya zip(*[iter(s)]*n)
, ia mengekstrak item dari ketiga iterator dari daftar secara berurutan. Karena semua iterator adalah objek yang sama, itu hanya mengelompokkan daftar dalam potongan n
.
Satu kata nasihat untuk menggunakan zip dengan cara ini. Ini akan memotong daftar Anda jika panjangnya tidak dapat dibagi rata. Untuk mengatasi ini, Anda dapat menggunakan itertools.izip_longest jika Anda dapat menerima nilai isian. Atau Anda bisa menggunakan sesuatu seperti ini:
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:], ]
Pemakaian:
for ints in n_split(range(1,12), 3):
print ', '.join([str(i) for i in ints])
Cetakan:
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
itertools
resep: docs.python.org/2/library/itertools.html#recipes grouper
. Tidak perlu menemukan kembali roda
Mungkin lebih mudah untuk melihat apa yang terjadi dengan interpreter python atau ipython
dengan n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Jadi, kami memiliki daftar dua iterator yang menunjuk ke objek iterator yang sama. Ingatlah bahwa iter
pada suatu objek mengembalikan objek iterator dan dalam skenario ini, itu adalah iterator yang sama dua kali karena *2
gula sintaksis python. Iterator juga hanya berjalan sekali.
Selanjutnya, zip
mengambil sejumlah iterable ( urutan adalah iterable ) dan membuat tupel dari elemen ke-i dari masing-masing urutan masukan. Karena kedua iterator identik dalam kasus kita, zip memindahkan iterator yang sama dua kali untuk setiap tupel 2-elemen keluaran.
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.
Operator unpacking ( *
) memastikan bahwa iterator berjalan hingga habis yang dalam hal ini sampai tidak ada cukup input untuk membuat tupel 2 elemen.
Ini dapat diperluas ke nilai apa pun n
dan zip(*[iter(s)]*n)
berfungsi seperti yang dijelaskan.
*
hanyalah kemudahan untuk menduplikasi objek. Cobalah dengan skalar dan kemudian dengan daftar. Juga mencoba print(*zip(*[iter("ABCDEFG")]*2))
vs print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Kemudian mulailah membagi keduanya menjadi langkah-langkah yang lebih kecil untuk melihat apa sebenarnya objek iterator dalam kedua pernyataan tersebut.