การมอบหมายงานกับ Python list slice อย่างไร


102

Python doc บอกว่าการแบ่งส่วนรายการจะส่งคืนรายการใหม่
ตอนนี้หากมีการส่งคืนรายการ "ใหม่" ฉันมีคำถามต่อไปนี้ที่เกี่ยวข้องกับ "การมอบหมายให้กับชิ้นส่วน"

a = [1, 2, 3]
a[0:2] = [4, 5]
print a

ตอนนี้ผลลัพธ์จะเป็น:

[4, 5, 3] 
  1. สิ่งที่ส่งคืนบางสิ่งมาทางด้านซ้ายของการแสดงออกได้อย่างไร?
  2. ใช่ฉันอ่านเอกสารแล้วและบอกว่าเป็นไปได้แล้วเนื่องจากการแบ่งส่วนรายการจะส่งคืนรายการ "ใหม่" เหตุใดจึงมีการแก้ไขรายการเดิม ฉันไม่สามารถเข้าใจกลไกที่อยู่เบื้องหลังมัน

@Mark Longair ขอโทษฉันคิดว่ารหัสเท่านั้นที่ควรจะถูกฟอร์แมตไม่ใช่ผลลัพธ์
Kartik Anand


7
คุณเข้าใจว่าa[0] = 4จะทำอย่างไร?
Josh Lee

1
การกำหนด @KartikAnand Slice เป็นสถานการณ์พิเศษที่ไม่มีการสร้างรายการใหม่ การสร้างออบเจ็กต์โดยไม่มีการผูกชื่อทางด้านซ้ายของ an นั้นไม่สมเหตุสมผล=ดังนั้นแทนที่จะทิ้งสิ่งนี้เป็นไวยากรณ์ที่ไม่ถูกต้อง python จะเปลี่ยนเป็นสิ่งที่คุณคาดหวังมากขึ้น เนื่องจาก python ไม่มีการอ้างอิงจึงไม่สามารถให้ผลลัพธ์ของชิ้นส่วนเปลี่ยนรายการเดิมได้ คุณได้รับสำเนา หากคุณให้ข้อมูลเพิ่มเติมเกี่ยวกับแอปพลิเคชันของคุณเราอาจช่วยให้คุณทำสิ่งต่างๆในรูปแบบ 'pythonic' ได้ดีขึ้น :)
Casey Kuball

1
@Darthfett ฉันไม่ได้ทำงานกับแอปพลิเคชันใด ๆ ในตอนนี้ แต่ฉันกำลังสอนตัวเอง python ก่อนที่ฉันจะเริ่มสกปรก :)
Kartik Anand

คำตอบ:


117

คุณกำลังสับสนระหว่างการดำเนินการสองอย่างที่ใช้ไวยากรณ์ที่คล้ายกันมาก:

1) การหั่น:

b = a[0:2]

นี้จะทำให้สำเนาของชิ้นที่และกำหนดที่จะab

2) การมอบหมายชิ้นงาน:

a[0:2] = b

สิ่งนี้แทนที่ส่วนของaเนื้อหาของb.

แม้ว่าไวยากรณ์จะคล้ายกัน (ฉันจินตนาการตามการออกแบบ!) การดำเนินการสองอย่างนี้แตกต่างกัน


4
นั่นคือสิ่งที่ฉันสงสัยในกรณีที่สองทำไมไม่เป็นส่วนของรายการใหม่ ??
Kartik Anand

12
@KartikAnand เพราะมันไม่ใช่ นั่นไม่ใช่สิ่งที่ภาษาระบุ
Marcin

เพื่อความชัดเจน "ใช้ชิ้นส่วน" หมายความว่า "ทำสำเนาชิ้นส่วน" ซึ่งเป็นส่วนหนึ่งของความสับสน
Mark Ransom

2
@KartikAnand: โดยทั่วไปใช่ ล่ามรู้ว่าอะไรคือสิ่งใดและจัดการอย่างเหมาะสม
NPE

1
@Dubslow: คุณสามารถทำได้โดยใช้โมดูลitertools สำหรับกรณีของคุณใช้ฟังก์ชั่นisliceด้วย,start=1 stop=Noneวิธีนี้จะหลีกเลี่ยงการทำสำเนาใด ๆ และใช้การประเมินแบบขี้เกียจ (ในกรณีของคุณขี้เกียจเข้าถึงรายการต้นฉบับ)
Spiros

69

เมื่อคุณระบุaทางด้านซ้ายของตัว=ดำเนินการคุณกำลังใช้การกำหนดปกติของ Python ซึ่งจะเปลี่ยนชื่อaในบริบทปัจจุบันเพื่อชี้ไปที่ค่าใหม่ สิ่งนี้ไม่เปลี่ยนค่าก่อนหน้าซึ่งaชี้ไป

โดยระบุa[0:2]บนด้านซ้ายของ=ผู้ประกอบการที่คุณจะบอกหลามคุณต้องการใช้การกำหนด Slice Slice Assignment เป็นไวยากรณ์พิเศษสำหรับรายการที่คุณสามารถแทรกลบหรือแทนที่เนื้อหาจากรายการ:

การแทรก :

>>> a = [1, 2, 3]
>>> a[0:0] = [-3, -2, -1, 0]
>>> a
[-3, -2, -1, 0, 1, 2, 3]

การลบ :

>>> a
[-3, -2, -1, 0, 1, 2, 3]
>>> a[2:4] = []
>>> a
[-3, -2, 1, 2, 3]

ทดแทน :

>>> a
[-3, -2, 1, 2, 3]
>>> a[:] = [1, 2, 3]
>>> a
[1, 2, 3]

บันทึก:

ความยาวของชิ้นงานอาจแตกต่างจากความยาวของลำดับที่กำหนดดังนั้นการเปลี่ยนความยาวของลำดับเป้าหมายหากลำดับเป้าหมายอนุญาต - แหล่งที่มา

การกำหนด Slice ให้ฟังก์ชั่นที่คล้ายกับTuple แกะกล่อง ตัวอย่างเช่นa[0:1] = [4, 5]เทียบเท่ากับ:

# Tuple Unpacking
a[0], a[1] = [4, 5]

ด้วย Tuple Unpacking คุณสามารถแก้ไขรายการที่ไม่ใช่ลำดับได้:

>>> a
[4, 5, 3]
>>> a[-1], a[0] = [7, 3]
>>> a
[3, 5, 7]

อย่างไรก็ตามการแกะกล่องทูเพิลนั้น จำกัด เฉพาะการเปลี่ยนเนื่องจากคุณไม่สามารถแทรกหรือลบองค์ประกอบได้

ก่อนและหลังการดำเนินการทั้งหมดนี้aเป็นรายการเดียวกัน Python เพียงแค่ให้น้ำตาลที่ดีในการปรับเปลี่ยนรายการในสถานที่


6
คล้ายกัน แต่ไม่เหมือนกันเนื่องจากคุณสามารถมีจำนวนองค์ประกอบที่ไม่เท่ากันได้ทางซ้ายและขวา
Mark Ransom

@MarkRansom นั่นเป็นจุดที่ยอดเยี่ยมฉันได้เพิ่มข้อมูลเพิ่มเติมเพื่อให้ชัดเจน
Casey Kuball

2
คือa[:] = some_listเทียบเท่ากับa = some_list[:]หรือa = some_list?
jadkik94

2
@ jadkik94 เนอะ a[:] = some_listชุดองค์ประกอบของทุกที่จะเป็นผู้a some_listการทำอย่างใดอย่างหนึ่งที่คุณพูดถึงจะเปลี่ยนสิ่งที่aเป็นอยู่ ตัวอย่างเช่น: a = [1, 2, 3] b = a a[:] = [4, 5, 6] a is b. บรรทัดสุดท้ายจะเป็นเท็จถ้าค่าของมันเปลี่ยนไปaแทนที่จะกลายพันธุ์
Casey Kuball

@CaseyKuball ค่าของstart: stop: stepควรจะเป็นบวกเสมอเมื่อเราทำการมอบหมายชิ้นส่วนหรือไม่?
strikersps

26

ฉันเจอคำถามเดียวกันมาก่อนและเกี่ยวข้องกับข้อกำหนดภาษา ตามที่ได้รับมอบหมายงบ ,

  1. หากด้านซ้ายของการมอบหมายเป็นการสมัครสมาชิก Python จะเรียก__setitem__ใช้วัตถุนั้น a[i] = xเทียบเท่ากับa.__setitem__(i, x).

  2. หากด้านซ้ายของการมอบหมายเป็นสไลซ์ Python ก็จะเรียก__setitem__ด้วยเช่นกัน แต่มีอาร์กิวเมนต์ต่างกัน: a[1:4]=[1,2,3]เทียบเท่ากับ a.__setitem__(slice(1,4,None), [1,2,3])

นั่นเป็นเหตุผลที่รายการ slice ทางด้านซ้ายของ '=' ทำงานแตกต่างกัน


4

การแบ่งส่วนทางด้านซ้ายมือของการดำเนินการมอบหมายคุณกำลังระบุรายการที่จะมอบหมาย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.