zip (* [iter (s)] * n) ทำงานอย่างไรใน Python


103
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)]

วิธีการzip(*[iter(s)]*n)ทำงานหรือไม่ จะเป็นอย่างไรหากเขียนด้วยรหัสแบบละเอียดมากกว่านี้


1
ดูวิธีการทำงานได้ที่นี่ด้วย: stackoverflow.com/questions/2202461/…
Matt Joiner

หากคำตอบไม่เพียงพอฉันบล็อกไว้ที่นี่: telliott99.blogspot.com/2010/01/…
telliott99

7
แม้ว่าจะน่าสนใจมาก แต่เทคนิคนี้ต้องเทียบกับค่า "ความสามารถในการอ่าน" หลักของ Python!
Demis

คำตอบ:


108

iter()เป็นตัววนซ้ำตามลำดับ [x] * nผลิตรายการที่มีnปริมาณของxคือรายการความยาวที่แต่ละองค์ประกอบn แยกลำดับเป็นอาร์กิวเมนต์สำหรับการเรียกใช้ฟังก์ชัน ดังนั้นคุณจะส่งตัววนซ้ำเดิม 3 ครั้งไปและมันจะดึงรายการจากตัววนซ้ำทุกครั้งx*argzip()

x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)

1
นานาน่ารู้:เมื่อตัววนซ้ำyields (= returns) รายการคุณสามารถจินตนาการว่ารายการนี้ "ใช้ไปแล้ว" ดังนั้นในครั้งต่อไปที่มีการเรียกตัววนซ้ำระบบจะให้รายการ "ไม่บริโภค" ถัดไป
winklerrr

46

คำตอบที่ดีอื่น ๆ และแสดงความคิดเห็นกันอธิบายบทบาทของการโต้แย้งการเปิดออกและซิป ()

ดังที่Ignacioและujukatzelกล่าวคุณจะส่งต่อzip()การอ้างอิงสามรายการไปยังตัววนซ้ำเดียวกันและzip()สร้าง 3 tuples ของจำนวนเต็มตามลำดับ - จากการอ้างอิงแต่ละรายการไปยัง 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
^                    ^                    ^            
      ^                    ^                    ^
            ^                    ^                    ^

และเนื่องจากคุณขอตัวอย่างโค้ด verbose เพิ่มเติม:

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

ปฏิบัติตามค่าของstartและend:

[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]

FWIW คุณจะได้ผลลัพธ์เดียวกันmap()ด้วยอาร์กิวเมนต์เริ่มต้นของNone:

>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]

สำหรับข้อมูลเพิ่มเติมzip()และmap(): http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/


31

ฉันคิดว่าสิ่งหนึ่งที่พลาดไปในคำตอบทั้งหมด (อาจชัดเจนสำหรับผู้ที่คุ้นเคยกับการย้ำซ้ำ) แต่คนอื่นไม่ชัดเจนนักคือ -

เนื่องจากเรามีตัววนซ้ำเหมือนกันมันจึงถูกใช้ไปและองค์ประกอบที่เหลือจะถูกใช้โดย zip ดังนั้นถ้าเราใช้แค่รายการไม่ใช่ iter เช่น

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

การใช้ตัววนซ้ำจะแสดงค่าและเก็บเฉพาะที่เหลืออยู่ดังนั้นสำหรับ zip เมื่อใช้ 0 1 แล้วจะพร้อมใช้งาน 2 และอื่น ๆ เป็นสิ่งที่บอบบาง แต่ค่อนข้างฉลาด !!!


+1 คุณช่วยฉันไว้! ฉันไม่อยากจะเชื่อเลยว่าคำตอบอื่น ๆ ได้ข้ามรายละเอียดที่สำคัญนี้ไปโดยถือว่าทุกคนรู้เรื่องนี้ คุณสามารถอ้างอิงถึงเอกสารประกอบที่มีข้อมูลนี้ได้หรือไม่?
Snehasish Karmakar

9

iter(s) ส่งคืนตัววนซ้ำสำหรับ s

[iter(s)]*n สร้างรายการ n คูณตัววนซ้ำเดียวกันสำหรับ s

ดังนั้นเมื่อทำzip(*[iter(s)]*n)มันจะแยกรายการจากตัวทำซ้ำทั้งสามรายการจากรายการตามลำดับ เนื่องจากตัววนซ้ำทั้งหมดเป็นวัตถุเดียวกันจึงจัดกลุ่มรายการเป็นชิ้น ๆnเท่านั้น


7
ไม่ใช่ 'n ตัววนซ้ำของรายการเดียวกัน' แต่เป็น 'n คูณวัตถุตัววนซ้ำเดียวกัน' ออบเจ็กต์ตัววนซ้ำที่แตกต่างกันจะไม่แชร์สถานะแม้ว่าจะอยู่ในรายการเดียวกันก็ตาม
Thomas Wouters

ขอบคุณแก้ไขแล้ว นั่นคือสิ่งที่ฉันกำลัง "คิด" แต่เขียนอย่างอื่น
sttwister

6

คำแนะนำหนึ่งคำสำหรับการใช้ zip ด้วยวิธีนี้ มันจะตัดรายการของคุณถ้าความยาวหารเท่ากันไม่ได้ ในการแก้ไขปัญหานี้คุณสามารถใช้itertools.izip_longestถ้าคุณสามารถยอมรับค่าการเติม หรือคุณสามารถใช้สิ่งนี้:

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:], ]

การใช้งาน:

for ints in n_split(range(1,12), 3):
    print ', '.join([str(i) for i in ints])

พิมพ์:

1, 2, 3
4, 5, 6
7, 8, 9
10, 11

3
นี้เป็นเอกสารที่มีอยู่แล้วในitertoolsสูตร: docs.python.org/2/library/itertools.html#recipes grouperไม่จำเป็นต้องสร้างล้อใหม่
jamylak

1

มันอาจจะง่ายกว่าที่จะเห็นว่าเกิดอะไรขึ้นในล่าม python หรือipythonด้วยn = 2:

In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]

ดังนั้นเราจึงมีรายการตัวทำซ้ำสองรายการซึ่งชี้ไปที่วัตถุตัววนซ้ำเดียวกัน โปรดจำไว้ว่าiterบนวัตถุส่งคืนอ็อบเจ็กต์ตัววนซ้ำและในสถานการณ์นี้มันเป็นตัววนซ้ำสองครั้งเนื่องจาก*2น้ำตาลวากยสัมพันธ์ของไพ ธ อน ตัวทำซ้ำยังทำงานเพียงครั้งเดียว

นอกจากนี้ให้zipใช้จำนวนการวนซ้ำจำนวนเท่าใดก็ได้ ( ลำดับคือการวนซ้ำ ) และสร้างทูเปิลจากองค์ประกอบ i ของแต่ละลำดับอินพุต เนื่องจากตัววนซ้ำทั้งสองเหมือนกันในกรณีของเรา zip จึงย้ายตัววนซ้ำเดียวกันสองครั้งสำหรับทูเพิล 2 องค์ประกอบแต่ละรายการ

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.

ตัวดำเนินการunpacking ( *)ช่วยให้มั่นใจได้ว่าตัววนซ้ำทำงานจนหมดซึ่งในกรณีนี้ก็คือจนกว่าจะไม่มีอินพุตเพียงพอที่จะสร้างทูเพิล 2 องค์ประกอบ

สิ่งนี้สามารถขยายเป็นมูลค่าnและใช้zip(*[iter(s)]*n)งานได้ตามที่อธิบายไว้


ขออภัยที่ตอบช้า แต่คุณช่วยอธิบายได้ไหมว่า "ตัวทำซ้ำตัวเดียวกันสองครั้งเนื่องจากน้ำตาลไวยากรณ์ python * 2 ตัวทำซ้ำยังทำงานเพียงครั้งเดียว" กรุณาส่วน? ถ้าเป็นเช่นนั้นผลลัพธ์จะไม่เป็นอย่างไร [("A", "A") .... ]? ขอบคุณ.
Bowen Liu

@BowenLiu *เป็นเพียงความสะดวกในการทำซ้ำวัตถุ ลองใช้ด้วยสเกลาร์และรายการ ลองprint(*zip(*[iter("ABCDEFG")]*2))เทียบกับprint(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")])). จากนั้นเริ่มแยกทั้งสองออกเป็นขั้นตอนเล็ก ๆ เพื่อดูว่าวัตถุตัววนซ้ำที่แท้จริงในสองคำสั่งคืออะไร
akhan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.