ฉันวนซ้ำรายการของ tuples ใน Python และพยายามที่จะลบออกหากพวกเขามีคุณสมบัติตรงตามเกณฑ์ที่กำหนด
for tup in somelist:
if determine(tup):
code_to_remove_tup
ฉันควรใช้code_to_remove_tup
อะไรแทน ฉันไม่สามารถหาวิธีลบรายการในแบบนี้ได้
ฉันวนซ้ำรายการของ tuples ใน Python และพยายามที่จะลบออกหากพวกเขามีคุณสมบัติตรงตามเกณฑ์ที่กำหนด
for tup in somelist:
if determine(tup):
code_to_remove_tup
ฉันควรใช้code_to_remove_tup
อะไรแทน ฉันไม่สามารถหาวิธีลบรายการในแบบนี้ได้
คำตอบ:
คุณสามารถใช้ list comprehension เพื่อสร้างรายการใหม่ที่มีเฉพาะองค์ประกอบที่คุณไม่ต้องการลบ:
somelist = [x for x in somelist if not determine(x)]
หรือโดยการกำหนดให้ชิ้นsomelist[:]
คุณสามารถกลายพันธุ์รายการที่มีอยู่เพื่อให้มีเพียงรายการที่คุณต้องการ
somelist[:] = [x for x in somelist if not determine(x)]
วิธีการนี้อาจมีประโยชน์หากมีการอ้างอิงอื่น ๆsomelist
ที่จำเป็นต้องสะท้อนถึงการเปลี่ยนแปลง
itertools
แทนที่จะเข้าใจคุณยังสามารถใช้ ใน Python 2:
from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)
หรือใน Python 3:
from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)
เพื่อความชัดเจนและสำหรับผู้ที่พบการใช้[:]
สัญกรณ์แฮ็คหรือฟัซซี่นี่เป็นทางเลือกที่ชัดเจนยิ่งขึ้น ในทางทฤษฎีมันควรทำแบบเดียวกันกับพื้นที่และเวลามากกว่าหนึ่งตอร์ปิโดด้านบน
temp = []
while somelist:
x = somelist.pop()
if not determine(x):
temp.append(x)
while temp:
somelist.append(templist.pop())
นอกจากนี้ยังทำงานในภาษาอื่น ๆ ที่อาจไม่มีความสามารถในการแทนที่รายการของรายการ Python ด้วยการปรับเปลี่ยนน้อยที่สุด ยกตัวอย่างเช่นไม่ใช่ทุกภาษาที่จะทำรายการที่ว่างเปล่าให้False
เหมือนกับที่ Python ทำ คุณสามารถใช้แทนสำหรับบางสิ่งบางอย่างชัดเจนมากขึ้นเช่นwhile somelist:
while len(somelist) > 0:
somelist[:] = (x for x in somelist if determine(x))
สิ่งนี้เพื่อสร้างเครื่องมือสร้างที่ไม่สามารถสร้างสำเนาที่ไม่จำเป็น
list_ass_slice()
ฟังก์ชั่นที่ใช้somelist[:]=
โทรPySequence_Fast()
ภายใน ฟังก์ชั่นนี้จะส่งคืนรายการเช่น@Alex Martelli โซลูชั่นที่ใช้รายการแทนเครื่องกำเนิดไฟฟ้าอาจมีประสิทธิภาพมากขึ้น
somelist
จะถูกทำให้กลายพันธุ์ในทั้งสองวิธีหรือไม่
คำตอบที่แนะนำการเข้าใจความหมายของรายการนั้นถูกต้องเกือบทุกประการยกเว้นว่าพวกเขาจะสร้างรายการใหม่ที่สมบูรณ์และจากนั้นให้ชื่อเดิมเป็นชื่อเดิมเหมือนกับที่พวกเขาไม่ได้แก้ไขรายการเก่าในสถานที่ มันแตกต่างจากสิ่งที่คุณทำโดยการลบแบบเลือกเช่นเดียวกับในข้อเสนอแนะของ @ Lennart - เร็วกว่า แต่ถ้ารายชื่อของคุณเข้าถึงได้ผ่านการอ้างอิงหลายรายการความจริงที่ว่าคุณเพิ่งทำการกู้คืนข้อมูลอ้างอิงหนึ่งรายการและไม่แก้ไขวัตถุรายการ ตัวเองสามารถนำไปสู่ข้อบกพร่องที่ลึกซึ้งและหายนะ
โชคดีที่มันง่ายมากที่จะได้รับทั้งความเร็วของความเข้าใจในรายการและความหมายที่จำเป็นของการเปลี่ยนแปลงแบบแทนที่ - รหัสเพียง:
somelist[:] = [tup for tup in somelist if determine(tup)]
สังเกตความแตกต่างเล็กน้อยกับคำตอบอื่น ๆ : อันนี้ไม่ได้กำหนดให้กับ barename - มันกำหนดให้กับชิ้นส่วนที่เพิ่งจะเป็นรายการทั้งหมดดังนั้นแทนที่เนื้อหา รายการภายในวัตถุรายการ Python เดียวกันแทนที่จะเพียงแค่ทำการอ้างอิงหนึ่งครั้งใหม่ (จากรายการวัตถุก่อนหน้าไปยังวัตถุรายการใหม่) เช่นคำตอบอื่น ๆ
a
โดยเนื้อหาของ Dict ใช้b
a.clear(); a.update(b)
x = ['foo','bar','baz']; y = x; x = [item for item in x if determine(item)];
reassigns นี้x
ถึงผลของการเข้าใจในรายชื่อ แต่y
ยังหมายถึงต้นฉบับ['foo','bar','baz']
รายการ หากคุณคาดหวังx
และy
อ้างถึงรายการเดียวกันคุณอาจแนะนำบั๊ก x = ["foo","bar","baz"]; y = x; x[:] = [item for item in x if determine(item)];
คุณป้องกันโดยมอบหมายให้ส่วนแบ่งของรายชื่อทั้งหมดในขณะที่อเล็กซ์แสดงและผมแสดงที่นี่: รายการมีการแก้ไขในสถานที่ ตรวจสอบให้แน่ใจว่าการอ้างอิงทั้งหมดไปยังรายการ (ทั้งที่นี่x
และy
ที่นี่) อ้างอิงถึงรายการใหม่
filter
ฟังก์ชั่นก็สร้างรายการใหม่ไม่ได้แก้ไของค์ประกอบในสถานที่ ... เท่านั้นolist[:] = [i for i in olist if not dislike(i)]
คุณต้องคัดลอกรายการและวนซ้ำก่อนหรือการวนซ้ำจะล้มเหลวด้วยผลลัพธ์ที่ไม่คาดคิด
ตัวอย่าง (ขึ้นอยู่กับประเภทของรายการ):
for tup in somelist[:]:
etc....
ตัวอย่าง:
>>> somelist = range(10)
>>> for x in somelist:
... somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]
>>> somelist = range(10)
>>> for x in somelist[:]:
... somelist.remove(x)
>>> somelist
[]
list(somelist)
จะแปลง iterable เป็นรายการ somelist[:]
ทำสำเนาของวัตถุที่สนับสนุนการแบ่งส่วน ดังนั้นพวกเขาไม่จำเป็นต้องทำสิ่งเดียวกัน ในกรณีนี้ฉันต้องการทำสำเนาsomelist
วัตถุดังนั้นฉันจึงใช้[:]
remove()
ต้องผ่านรายการทั้งหมดสำหรับการวนซ้ำทุกครั้งดังนั้นจะใช้เวลาตลอดไป
for i in range(len(somelist) - 1, -1, -1):
if some_condition(somelist, i):
del somelist[i]
คุณต้องย้อนกลับไปไม่เช่นนั้นมันก็เหมือนกับการตัดกิ่งไม้ที่คุณกำลังนั่งอยู่ :-)
ผู้ใช้ Python 2: แทนที่range
ด้วยxrange
เพื่อหลีกเลี่ยงการสร้างรายการฮาร์ดโค้ด
reversed()
builtin
m
ช้าลง
บทช่วยสอน Python 2 อย่างเป็นทางการ 4.2 "สำหรับงบ"
https://docs.python.org/2/tutorial/controlflow.html#for-statements
เอกสารส่วนนี้ทำให้เห็นได้ชัดว่า:
[:]
หากคุณต้องการแก้ไขลำดับที่คุณวนซ้ำในขณะที่อยู่ในลูป (ตัวอย่างเช่นสำหรับรายการที่ซ้ำกันที่เลือก) ขอแนะนำให้คุณทำสำเนาก่อน การวนซ้ำตามลำดับไม่ได้ทำสำเนาโดยปริยาย สัญกรณ์ชิ้นทำให้สะดวกโดยเฉพาะอย่างยิ่ง:
>>> words = ['cat', 'window', 'defenestrate'] >>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words ['defenestrate', 'cat', 'window', 'defenestrate']
Python 2 เอกสาร 7.3 "คำสั่งสำหรับ"
https://docs.python.org/2/reference/compound_stmts.html#for
เอกสารส่วนนี้กล่าวอีกครั้งว่าคุณต้องทำสำเนาและให้ตัวอย่างการลบจริง:
หมายเหตุ: มีความละเอียดอ่อนเมื่อมีการแก้ไขลำดับโดยการวนรอบ (นี้สามารถเกิดขึ้นได้สำหรับลำดับที่ไม่แน่นอนคือรายการ) ตัวนับภายในจะใช้ในการติดตามรายการที่จะใช้ถัดไปและเพิ่มขึ้นในการทำซ้ำแต่ละครั้ง เมื่อตัวนับนี้มาถึงความยาวของลำดับลูปจะสิ้นสุดลง ซึ่งหมายความว่าหากชุดลบรายการปัจจุบัน (หรือก่อนหน้า) จากลำดับรายการถัดไปจะถูกข้าม (เนื่องจากได้รับดัชนีของรายการปัจจุบันที่ได้รับการปฏิบัติแล้ว) ในทำนองเดียวกันหากชุดแทรกรายการในลำดับก่อนรายการปัจจุบันรายการปัจจุบันจะได้รับการปฏิบัติอีกครั้งในครั้งต่อไปผ่านวง สิ่งนี้สามารถนำไปสู่ข้อผิดพลาดที่น่ารังเกียจที่สามารถหลีกเลี่ยงได้โดยการทำสำเนาชั่วคราวโดยใช้ชิ้นส่วนของลำดับทั้งหมดเช่น
for x in a[:]: if x < 0: a.remove(x)
อย่างไรก็ตามฉันไม่เห็นด้วยกับการใช้งานนี้เนื่องจาก.remove()
จะต้องวนซ้ำรายการทั้งหมดเพื่อค้นหาค่า
วิธีแก้ปัญหาที่ดีที่สุด
ทั้ง:
เริ่มต้นอาร์เรย์ใหม่ตั้งแต่เริ่มต้นและ.append()
กลับมาที่จุดสิ้นสุด: https://stackoverflow.com/a/1207460/895245
เวลานี้มีประสิทธิภาพ แต่มีพื้นที่น้อยกว่าที่มีประสิทธิภาพเพราะมันจะเก็บสำเนาของอาร์เรย์ในระหว่างการทำซ้ำ
ใช้del
กับดัชนี: https://stackoverflow.com/a/1207485/895245
นี้เป็นพื้นที่ที่มีประสิทธิภาพมากขึ้นเพราะมัน dispenses สำเนาอาร์เรย์ แต่มันเป็นเวลาที่มีประสิทธิภาพน้อยเพราะรายการ CPython จะดำเนินการกับอาร์เรย์แบบไดนามิก
ซึ่งหมายความว่าการลบรายการต้องเลื่อนรายการต่อไปนี้ทั้งหมดทีละรายการคือ O (N)
โดยทั่วไปคุณเพียงต้องการไปที่.append()
ตัวเลือกที่เร็วกว่าโดยค่าเริ่มต้นเว้นแต่หน่วยความจำจะเป็นเรื่องใหญ่
Python สามารถทำได้ดีกว่านี้ไหม
ดูเหมือนว่า Python API นี้จะสามารถปรับปรุงได้ เปรียบเทียบเช่นกับ:
std::vector::erase
ซึ่งส่งคืน interator ที่ถูกต้องไปยังองค์ประกอบหลังจากที่ลบออกทั้งสองอย่างทำให้ชัดเจนว่าคุณไม่สามารถแก้ไขรายการที่ถูกทำซ้ำยกเว้นตัววนซ้ำเองและให้วิธีที่มีประสิทธิภาพในการทำเช่นนั้นโดยไม่ต้องคัดลอกรายการ
บางทีอาจจะเป็นเหตุผลพื้นฐานที่รายการหลามจะถือว่าเป็นอาร์เรย์แบบไดนามิกได้รับการสนับสนุนและดังนั้นจึงประเภทของการกำจัดใด ๆ จะเป็นเวลาที่ไม่มีประสิทธิภาพ anyways ขณะที่ Java มีลำดับชั้นของอินเตอร์เฟซที่ดีกว่าที่มีทั้งArrayList
และการใช้งานของLinkedList
ListIterator
ดูเหมือนจะไม่มีประเภทรายการเชื่อมโยงที่ชัดเจนใน Python stdlib เช่น: Python Linked List
แนวทางที่ดีที่สุดของคุณสำหรับตัวอย่างเช่นนั้นคือความเข้าใจในรายการ
somelist = [tup for tup in somelist if determine(tup)]
ในกรณีที่คุณกำลังทำอะไรที่ซับซ้อนกว่าการเรียกdetermine
ฟังก์ชั่นฉันชอบสร้างรายการใหม่และต่อท้ายเมื่อฉันไป ตัวอย่างเช่น
newlist = []
for tup in somelist:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
somelist = newlist
การคัดลอกรายการโดยใช้remove
อาจทำให้โค้ดของคุณดูสะอาดกว่าดังที่อธิบายไว้ในหนึ่งในคำตอบด้านล่าง คุณไม่ควรทำสิ่งนี้อย่างแน่นอนสำหรับรายการที่มีขนาดใหญ่มากเนื่องจากสิ่งนี้เกี่ยวข้องกับการคัดลอกรายการทั้งหมดและดำเนินO(n)
remove
การสำหรับการลบองค์ประกอบแต่ละรายการทำให้O(n^2)
ขั้นตอนวิธี
for tup in somelist[:]:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
สำหรับผู้ที่ชอบการเขียนโปรแกรมการทำงาน:
somelist[:] = filter(lambda tup: not determine(tup), somelist)
หรือ
from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
filter
ดีและ Pythonic มากกว่า 2. หากคุณต้องการlambda
ใช้งานmap
หรือfilter
คอมพ์ list หรือ genexpr เป็นตัวเลือกที่ดีกว่าเสมอ map
และfilter
อาจเร็วขึ้นเล็กน้อยเมื่อฟังก์ชันการแปลง / เพรดิเคตเป็น Python ในตัวที่ติดตั้งใน C และการทำซ้ำไม่ได้มีขนาดเล็ก แต่มักจะช้ากว่าเสมอเมื่อคุณต้องการlambda
listcomp / genexpr ที่สามารถหลีกเลี่ยงได้
ฉันต้องทำสิ่งนี้ด้วยรายการที่มีขนาดใหญ่และการทำซ้ำรายการดูเหมือนจะแพงโดยเฉพาะอย่างยิ่งเนื่องจากในกรณีของฉันจำนวนการลบจะน้อยเมื่อเทียบกับรายการที่เหลืออยู่ ฉันใช้วิธีการระดับต่ำนี้
array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
if someTest(array[i]):
del array[i]
arraySize -= 1
else:
i += 1
สิ่งที่ฉันไม่ทราบก็คือการลบสองสามรายการที่มีประสิทธิภาพเมื่อเปรียบเทียบกับการคัดลอกรายการขนาดใหญ่ โปรดแสดงความคิดเห็นหากคุณมีข้อมูลเชิงลึกใด ๆ
list
เป็นโครงสร้างข้อมูลในสถานที่แรกควรพิจารณาอย่างรอบคอบเนื่องจากการลบออกจากกลางรายการจะใช้เวลาเชิงเส้นตรงตามความยาวของรายการ หากคุณไม่จำเป็นต้องเข้าถึงไอเท็มตามลำดับแบบ k-th แบบสุ่มอาจพิจารณาได้OrderedDict
หรือไม่
newlist = []
แล้วnewlist.append(array[i])
ก็ก่อนdel array[i]
?
list()
เป็นรายการที่เชื่อมโยงการเข้าถึงแบบสุ่มมีราคาแพงถ้าlist()
เป็นอาร์เรย์การลบจะมีราคาแพงเนื่องจากพวกเขาต้องการย้ายองค์ประกอบต่อไปนี้ทั้งหมดไปข้างหน้า ตัวทำซ้ำที่เหมาะสมสามารถทำให้สิ่งต่าง ๆ ดีสำหรับการใช้งานรายการที่เชื่อมโยง อย่างไรก็ตามนี่อาจเป็นพื้นที่ที่มีประสิทธิภาพ
อาจเป็นเรื่องที่ฉลาดที่จะสร้างรายการใหม่ถ้ารายการในปัจจุบันตรงกับเกณฑ์ที่ต้องการ
ดังนั้น:
for item in originalList:
if (item != badValue):
newList.append(item)
และเพื่อหลีกเลี่ยงการโค้ดใหม่โครงการทั้งหมดด้วยชื่อรายการใหม่:
originalList[:] = newList
บันทึกจากเอกสาร Python:
copy.copy (x) ส่งคืนสำเนาตื้น ๆ ของ x
copy.deepcopy (x) ส่งคืนสำเนาลึกของ x
แต่เดิมคำตอบนี้ถูกเขียนขึ้นเพื่อตอบคำถามที่ถูกทำเครื่องหมายว่าซ้ำกัน: การลบพิกัดออกจากรายการบนไพ ธ อน
รหัสของคุณมีสองปัญหา:
1) เมื่อใช้ remove () คุณพยายามลบจำนวนเต็มในขณะที่คุณต้องการลบ tuple
2) The for loop จะข้ามรายการในรายการของคุณ
ลองทำสิ่งที่เกิดขึ้นเมื่อเราเรียกใช้รหัสของคุณ:
>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
... if a < 0 or b < 0:
... L1.remove(a,b)
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)
ปัญหาแรกคือคุณผ่านทั้ง 'a' และ 'b' เพื่อลบ () แต่ remove () ยอมรับอาร์กิวเมนต์เพียงตัวเดียวเท่านั้น ดังนั้นเราจะลบ () เพื่อทำงานอย่างถูกต้องกับรายการของคุณได้อย่างไร เราจำเป็นต้องทราบว่าแต่ละองค์ประกอบของรายการของคุณคืออะไร ในกรณีนี้แต่ละคนเป็นสิ่งอันดับ หากต้องการดูสิ่งนี้ให้เข้าถึงองค์ประกอบหนึ่งของรายการ (การทำดัชนีเริ่มต้นที่ 0):
>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>
Aha! แต่ละองค์ประกอบของ L1 เป็น tuple นั่นคือสิ่งที่เราต้องผ่านเพื่อลบ () Tuples ใน python นั้นง่ายมากมันถูกสร้างขึ้นโดยใส่ค่าไว้ในวงเล็บ "a, b" ไม่ใช่ tuple แต่ "(a, b)" เป็น tuple ดังนั้นเราจึงแก้ไขโค้ดของคุณและรันอีกครั้ง:
# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))
รหัสนี้ทำงานโดยไม่มีข้อผิดพลาด แต่ให้ดูที่รายการผลลัพธ์:
L1 is now: [(1, 2), (5, 6), (1, -2)]
ทำไม (1, -2) ยังอยู่ในรายการของคุณ ปรากฎว่าการแก้ไขรายการในขณะที่ใช้การวนซ้ำเพื่อวนซ้ำเป็นความคิดที่แย่มากโดยไม่ต้องดูแลเป็นพิเศษ เหตุผลที่ (1, -2) ยังคงอยู่ในรายการคือที่ตั้งของแต่ละรายการในรายการเปลี่ยนไประหว่างการวนซ้ำของ for loop มาดูกันว่าจะเกิดอะไรขึ้นถ้าเราป้อนรหัสข้างต้นอีกต่อไป:
L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
เนื่องจากคุณสามารถอนุมานจากผลลัพธ์นั้นทุกครั้งที่คำสั่งเงื่อนไขประเมินเป็นจริงและรายการถูกลบออกการวนซ้ำครั้งถัดไปของลูปจะข้ามการประเมินไอเท็มถัดไปในรายการเนื่องจากค่าอยู่ในดัชนีที่แตกต่างกัน
ทางออกที่ง่ายที่สุดคือการคัดลอกรายการจากนั้นวนซ้ำในรายการเดิมและแก้ไขเฉพาะสำเนา คุณสามารถลองทำเช่นนี้:
L2 = L1
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)
อย่างไรก็ตามผลลัพธ์จะเหมือนก่อนหน้านี้:
'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
นี่เป็นเพราะเมื่อเราสร้าง L2, python ไม่ได้สร้างวัตถุใหม่ แต่มันอ้างอิง L2 ไปยังวัตถุเดียวกับ L1 แทน เราสามารถตรวจสอบสิ่งนี้ด้วย 'เป็น' ซึ่งแตกต่างจากเพียง "เท่ากับ" (==)
>>> L2=L1
>>> L1 is L2
True
เราสามารถทำสำเนาจริงโดยใช้ copy.copy () จากนั้นทุกอย่างทำงานตามที่คาดไว้:
import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
ในที่สุดก็มีทางออกที่สะอาดกว่าวิธีเดียวที่ต้องทำสำเนา L1 ใหม่ทั้งหมด ฟังก์ชัน reverse ():
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
if a < 0 or b < 0 :
L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
น่าเสียดายที่ฉันไม่สามารถอธิบายได้อย่างชัดเจนว่าการสลับกลับ () ทำงานอย่างไร มันจะส่งคืนวัตถุ 'listreverseiterator' เมื่อรายการถูกส่งผ่านไปยังมัน สำหรับวัตถุประสงค์ในทางปฏิบัติคุณสามารถคิดว่าเป็นการสร้างสำเนาของอาร์กิวเมนต์ที่กลับรายการ นี่คือทางออกที่ฉันแนะนำ
หากคุณต้องการทำสิ่งอื่นระหว่างการทำซ้ำอาจเป็นการดีที่จะได้ทั้งดัชนี (ซึ่งรับประกันว่าคุณจะสามารถอ้างอิงได้ตัวอย่างเช่นถ้าคุณมีรายการ dicts) และเนื้อหารายการในรายการจริง
inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]
for idx, i in enumerate(inlist):
do some stuff with i['field1']
if somecondition:
xlist.append(idx)
for i in reversed(xlist): del inlist[i]
enumerate
ช่วยให้คุณเข้าถึงรายการและดัชนีได้ในครั้งเดียว reversed
คือดัชนีที่คุณจะลบในภายหลังจะไม่เปลี่ยนแปลงในตัวคุณ
คำตอบส่วนใหญ่ที่นี่ต้องการให้คุณสร้างสำเนาของรายการ ฉันมีกรณีการใช้งานที่รายการค่อนข้างยาว (110K รายการ) และมันฉลาดกว่าที่จะลดรายการแทน
ก่อนอื่นคุณจะต้องเปลี่ยนห่วง foreach ด้วยในขณะที่วง ,
i = 0
while i < len(somelist):
if determine(somelist[i]):
del somelist[i]
else:
i += 1
ค่าของi
จะไม่เปลี่ยนแปลงในบล็อก if เนื่องจากคุณต้องการรับค่าของรายการใหม่จากดัชนีเดียวกันเมื่อรายการเก่าถูกลบ
คุณสามารถลองวนกลับกันได้เพื่อ some_list คุณจะทำสิ่งต่อไปนี้:
list_len = len(some_list)
for i in range(list_len):
reverse_i = list_len - 1 - i
cur = some_list[reverse_i]
# some logic with cur element
if some_condition:
some_list.pop(reverse_i)
วิธีนี้ดัชนีจะถูกจัดตำแหน่งและไม่ได้รับผลกระทบจากการอัปเดตรายการ
reversed(list(enumerate(some_list)))
จะง่ายกว่าการคำนวณดัชนีตัวเอง
วิธีแก้ปัญหาที่เป็นไปได้วิธีหนึ่งที่มีประโยชน์หากคุณไม่เพียง แต่ต้องการลบบางสิ่ง แต่ยังทำบางสิ่งกับองค์ประกอบทั้งหมดในการวนรอบเดียว:
alist = ['good', 'bad', 'good', 'bad', 'good']
i = 0
for x in alist[:]:
if x == 'bad':
alist.pop(i)
i -= 1
# do something cool with x or just print x
print(x)
i += 1
bad
สิ่งต่าง ๆ ให้ทำอะไรกับมันและทำบางอย่างกับgood
สิ่งต่าง ๆ ในวงเดียว
alist[:]
) และเนื่องจากคุณอาจทำสิ่งที่แฟนซีมันมีกรณีการใช้งานจริง การแก้ไขที่ดีนั้นดี รับคะแนนโหวตของฉัน
ฉันต้องทำสิ่งที่คล้ายกันและในกรณีของฉันปัญหาคือหน่วยความจำ - ฉันต้องรวมวัตถุหลายชุดข้อมูลในรายการหลังจากทำบางสิ่งกับพวกเขาเป็นวัตถุใหม่และจำเป็นต้องกำจัดแต่ละรายการที่ฉันรวม หลีกเลี่ยงการทำซ้ำทั้งหมดและทำให้หน่วยความจำหมด ในกรณีของฉันมีวัตถุในพจนานุกรมแทนที่จะเป็นรายการทำงานได้ดี:
`` `
k = range(5)
v = ['a','b','c','d','e']
d = {key:val for key,val in zip(k, v)}
print d
for i in range(5):
print d[i]
d.pop(i)
print d
`` `
TLDR:
ฉันเขียนห้องสมุดที่อนุญาตให้คุณทำสิ่งนี้:
from fluidIter import FluidIterable
fSomeList = FluidIterable(someList)
for tup in fSomeList:
if determine(tup):
# remove 'tup' without "breaking" the iteration
fSomeList.remove(tup)
# tup has also been removed from 'someList'
# as well as 'fSomeList'
เป็นการดีที่สุดที่จะใช้วิธีอื่นถ้าเป็นไปได้ซึ่งไม่จำเป็นต้องแก้ไขการวนซ้ำของคุณในขณะที่วนซ้ำ แต่สำหรับอัลกอริทึมบางอย่างมันอาจไม่ตรงไปตรงมา ดังนั้นถ้าคุณแน่ใจว่าคุณต้องการรูปแบบรหัสที่อธิบายไว้ในคำถามเดิมเป็นไปได้
ควรทำงานกับลำดับที่ไม่แน่นอนทั้งหมดไม่เพียงแสดง
คำตอบแบบเต็ม:
แก้ไข: ตัวอย่างรหัสสุดท้ายในคำตอบนี้ให้เคสใช้สำหรับสาเหตุที่บางครั้งคุณอาจต้องการแก้ไขรายการแทนการใช้ list comprehension ส่วนแรกของคำตอบทำหน้าที่เป็นตัวช่วยสอนวิธีการการปรับเปลี่ยนอาร์เรย์
วิธีการแก้ปัญหาต่อจากนี้คำตอบ (สำหรับคำถามที่เกี่ยวข้อง) จาก senderle ซึ่งจะอธิบายถึงวิธีการอัปเดตดัชนีอาร์เรย์ในขณะที่วนซ้ำผ่านรายการที่ได้รับการแก้ไข วิธีการแก้ปัญหาด้านล่างได้รับการออกแบบมาเพื่อติดตามดัชนีอาเรย์อย่างถูกต้องแม้ว่าจะมีการแก้ไขรายการ
ดาวน์โหลดfluidIter.py
จากที่นี่ https://github.com/alanbacon/FluidIterator
เป็นเพียงไฟล์เดียวดังนั้นไม่จำเป็นต้องติดตั้ง git ไม่มีตัวติดตั้งดังนั้นคุณจะต้องตรวจสอบให้แน่ใจว่าไฟล์นั้นอยู่ในเส้นทางของหลามด้วยตัวคุณเอง รหัสถูกเขียนขึ้นสำหรับ python 3 และไม่ได้ทดสอบกับ python 2
from fluidIter import FluidIterable
l = [0,1,2,3,4,5,6,7,8]
fluidL = FluidIterable(l)
for i in fluidL:
print('initial state of list on this iteration: ' + str(fluidL))
print('current iteration value: ' + str(i))
print('popped value: ' + str(fluidL.pop(2)))
print(' ')
print('Final List Value: ' + str(l))
สิ่งนี้จะสร้างผลลัพธ์ต่อไปนี้:
initial state of list on this iteration: [0, 1, 2, 3, 4, 5, 6, 7, 8]
current iteration value: 0
popped value: 2
initial state of list on this iteration: [0, 1, 3, 4, 5, 6, 7, 8]
current iteration value: 1
popped value: 3
initial state of list on this iteration: [0, 1, 4, 5, 6, 7, 8]
current iteration value: 4
popped value: 4
initial state of list on this iteration: [0, 1, 5, 6, 7, 8]
current iteration value: 5
popped value: 5
initial state of list on this iteration: [0, 1, 6, 7, 8]
current iteration value: 6
popped value: 6
initial state of list on this iteration: [0, 1, 7, 8]
current iteration value: 7
popped value: 7
initial state of list on this iteration: [0, 1, 8]
current iteration value: 8
popped value: 8
Final List Value: [0, 1]
ข้างต้นเราได้ใช้pop
วิธีการกับวัตถุรายการของเหลว วิธีการ iterable อื่น ๆ ทั่วไปจะดำเนินการยังเช่นdel fluidL[i]
, .remove
, .insert
, ,.append
.extend
รายการยังสามารถแก้ไขได้โดยใช้ชิ้น ( sort
และreverse
วิธีการไม่ได้นำไปใช้)
เงื่อนไขเดียวคือคุณต้องแก้ไขรายการในสถานที่เท่านั้นหาก ณ เวลาใด ๆfluidL
หรือl
ถูกกำหนดใหม่ให้กับวัตถุรายการอื่นรหัสจะไม่ทำงาน fluidL
วัตถุต้นฉบับจะยังคงถูกใช้โดย for for loop แต่จะไม่อยู่ในขอบเขตที่เราจะแก้ไข
กล่าวคือ
fluidL[2] = 'a' # is OK
fluidL = [0, 1, 'a', 3, 4, 5, 6, 7, 8] # is not OK
หากเราต้องการเข้าถึงค่าดัชนีปัจจุบันของรายการเราไม่สามารถใช้แจกแจงได้เนื่องจากนี่จะนับเฉพาะจำนวนครั้งที่วนรอบทำงาน แต่เราจะใช้วัตถุตัววนซ้ำโดยตรง
fluidArr = FluidIterable([0,1,2,3])
# get iterator first so can query the current index
fluidArrIter = fluidArr.__iter__()
for i, v in enumerate(fluidArrIter):
print('enum: ', i)
print('current val: ', v)
print('current ind: ', fluidArrIter.currentIndex)
print(fluidArr)
fluidArr.insert(0,'a')
print(' ')
print('Final List Value: ' + str(fluidArr))
สิ่งนี้จะเอาท์พุตดังนี้:
enum: 0
current val: 0
current ind: 0
[0, 1, 2, 3]
enum: 1
current val: 1
current ind: 2
['a', 0, 1, 2, 3]
enum: 2
current val: 2
current ind: 4
['a', 'a', 0, 1, 2, 3]
enum: 3
current val: 3
current ind: 6
['a', 'a', 'a', 0, 1, 2, 3]
Final List Value: ['a', 'a', 'a', 'a', 0, 1, 2, 3]
FluidIterable
ชั้นเพียงแค่ให้เสื้อคลุมสำหรับวัตถุรายการเดิม วัตถุต้นฉบับสามารถเข้าถึงได้เป็นคุณสมบัติของวัตถุของเหลวเช่น:
originalList = fluidArr.fixedIterable
ตัวอย่างเพิ่มเติม / การทดสอบสามารถพบได้ในส่วนที่ด้านล่างของif __name__ is "__main__":
fluidIter.py
สิ่งเหล่านี้น่าดูเพราะพวกเขาอธิบายสิ่งที่เกิดขึ้นในสถานการณ์ต่าง ๆ เช่น: การแทนที่ส่วนขนาดใหญ่ของรายการโดยใช้ชิ้น หรือใช้ (และปรับเปลี่ยน) iterable เดียวกันในซ้อนสำหรับลูป
ตามที่ฉันได้กล่าวไว้ในตอนแรก: นี่เป็นวิธีแก้ปัญหาที่ซับซ้อนซึ่งจะทำให้การอ่านรหัสของคุณเสียหายและทำให้ยากต่อการดีบัก ดังนั้นจึงควรพิจารณาโซลูชันอื่น ๆ เช่นรายการความเข้าใจที่กล่าวถึงในคำตอบของ David Raznick ก่อน ที่ถูกกล่าวว่าฉันได้พบครั้งที่ชั้นนี้มีประโยชน์กับฉันและใช้งานง่ายกว่าการติดตามดัชนีองค์ประกอบที่ต้องลบ
แก้ไข: ดังที่กล่าวไว้ในความคิดเห็นคำตอบนี้ไม่ได้นำเสนอปัญหาที่วิธีนี้มีวิธีแก้ปัญหา ฉันจะพยายามที่อยู่ที่นี่:
รายการความเข้าใจให้วิธีการสร้างรายการใหม่ แต่วิธีการเหล่านี้มีแนวโน้มที่จะดูแต่ละองค์ประกอบในการแยกมากกว่าสถานะปัจจุบันของรายการโดยรวม
กล่าวคือ
newList = [i for i in oldList if testFunc(i)]
แต่จะเกิดอะไรขึ้นถ้าผลลัพธ์ของการtestFunc
ขึ้นอยู่กับองค์ประกอบที่เพิ่มเข้าไปnewList
แล้ว? หรือองค์ประกอบยังคงอยู่ในoldList
ที่อาจจะเพิ่มต่อไป? อาจยังมีวิธีใช้ list comprehension แต่มันจะเริ่มสูญเสียความสง่างามและสำหรับฉันมันรู้สึกง่ายขึ้นในการปรับเปลี่ยนรายการในสถานที่
โค้ดด้านล่างเป็นตัวอย่างหนึ่งของอัลกอริทึมที่ทนทุกข์จากปัญหาข้างต้น อัลกอริทึมจะลดรายการเพื่อให้ไม่มีองค์ประกอบใดเป็นองค์ประกอบหลายอย่างขององค์ประกอบอื่น ๆ
randInts = [70, 20, 61, 80, 54, 18, 7, 18, 55, 9]
fRandInts = FluidIterable(randInts)
fRandIntsIter = fRandInts.__iter__()
# for each value in the list (outer loop)
# test against every other value in the list (inner loop)
for i in fRandIntsIter:
print(' ')
print('outer val: ', i)
innerIntsIter = fRandInts.__iter__()
for j in innerIntsIter:
innerIndex = innerIntsIter.currentIndex
# skip the element that the outloop is currently on
# because we don't want to test a value against itself
if not innerIndex == fRandIntsIter.currentIndex:
# if the test element, j, is a multiple
# of the reference element, i, then remove 'j'
if j%i == 0:
print('remove val: ', j)
# remove element in place, without breaking the
# iteration of either loop
del fRandInts[innerIndex]
# end if multiple, then remove
# end if not the same value as outer loop
# end inner loop
# end outerloop
print('')
print('final list: ', randInts)
เอาท์พุทและรายการที่ลดลงสุดท้ายแสดงอยู่ด้านล่าง
outer val: 70
outer val: 20
remove val: 80
outer val: 61
outer val: 54
outer val: 18
remove val: 54
remove val: 18
outer val: 7
remove val: 70
outer val: 55
outer val: 9
remove val: 18
final list: [20, 61, 7, 55, 9]
some_list[:] = [x for x in some_list if not some_condition(x)]
ไม่บรรลุผล หากไม่มีคำตอบนั้นทำไมทุกคนควรเชื่อว่าการดาวน์โหลดและใช้ห้องสมุด 600 บรรทัดของคุณสมบูรณ์ด้วยการพิมพ์ผิดและรหัสความคิดเห็นเป็นทางออกที่ดีกว่าสำหรับการแก้ปัญหาของพวกเขา -1
some_list[:] = [x for x in some_list if not some_condition(y)]
ที่เป็นองค์ประกอบที่แตกต่างจากรายการy
หรือมันจะเป็นไปได้ที่จะเขียนx
some_list[:] = [x for x in some_list if not some_condition(intermediateStateOf_some_list)]
วิธีที่มีประสิทธิภาพมากที่สุดคือรายการเข้าใจหลายคนแสดงกรณีของพวกเขาแน่นอนก็ยังเป็นวิธีที่ดีที่จะได้รับผ่านiterator
filter
Filter
รับฟังก์ชั่นและลำดับFilter
ใช้ฟังก์ชั่นการส่งผ่านไปแต่ละองค์ประกอบในทางกลับกันแล้วตัดสินใจว่าจะเก็บหรือทิ้งองค์ประกอบขึ้นอยู่กับว่าค่าตอบแทนการทำงานเป็นหรือTrue
False
มีตัวอย่าง (รับอัตราต่อรองใน tuple):
list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))
# result: [1, 5, 9, 15]
ข้อควรระวัง: คุณไม่สามารถจัดการตัววนซ้ำได้ บางครั้งการวนซ้ำนั้นดีกว่าลำดับ
สำหรับ loop จะวนซ้ำผ่าน index ..
คิดว่าคุณมีรายการ
[5, 7, 13, 29, 65, 91]
lis
คุณได้ใช้ตัวแปรรายการที่เรียกว่า และคุณใช้เหมือนกันเพื่อลบ ..
ตัวแปรของคุณ
lis = [5, 7, 13, 29, 35, 65, 91]
0 1 2 3 4 5 6
ในระหว่างการทำซ้ำ 5
หมายเลข 35ของคุณไม่ใช่หมายเลขเฉพาะดังนั้นคุณจึงลบออกจากรายการ
lis.remove(y)
จากนั้นค่าถัดไป (65) จะย้ายไปยังดัชนีก่อนหน้า
lis = [5, 7, 13, 29, 65, 91]
0 1 2 3 4 5
ดังนั้นตัวชี้การวนซ้ำที่ 4 จึงย้ายไปยังตำแหน่งที่ 5 ..
นั่นเป็นสาเหตุที่ลูปของคุณไม่ครอบคลุม 65 ตั้งแต่ถูกย้ายไปยังดัชนีก่อนหน้า
ดังนั้นคุณไม่ควรอ้างอิงรายการไปยังตัวแปรอื่นซึ่งยังคงอ้างถึงต้นฉบับแทนที่จะเป็นสำเนา
ite = lis #dont do it will reference instead copy
ทำสำเนารายการโดยใช้ list[::]
ตอนนี้คุณจะให้
[5, 7, 13, 29]
ปัญหาคือคุณลบค่าออกจากรายการระหว่างการวนซ้ำแล้วดัชนีรายการของคุณจะยุบ
เพื่อให้คุณสามารถลองใช้ความเข้าใจแทน
ซึ่งรองรับทั้งหมด iterable like, list, tuple, dict, string ฯลฯ
หากคุณต้องการลบองค์ประกอบจากรายการในขณะที่วนซ้ำให้ใช้ห่วงลูปเพื่อให้คุณสามารถเปลี่ยนดัชนีปัจจุบันและดัชนีสิ้นสุดหลังจากการลบแต่ละครั้ง
ตัวอย่าง:
i = 0
length = len(list1)
while i < length:
if condition:
list1.remove(list1[i])
i -= 1
length -= 1
i += 1
คำตอบอื่น ๆ นั้นถูกต้องว่าโดยปกติแล้วมันเป็นความคิดที่ดีที่จะลบออกจากรายการที่คุณกำลังวนซ้ำ ทำซ้ำย้อนกลับเพื่อหลีกเลี่ยงข้อผิดพลาด แต่ก็ยากที่จะทำตามรหัสที่ทำเช่นนั้นดังนั้นโดยทั่วไปแล้วคุณจะดีกว่าเมื่อใช้รายการความเข้าใจหรือfilter
แต่มันเป็นเรื่องยากมากที่จะปฏิบัติตามรหัสที่ไม่ว่าดังนั้นคุณมักจะดีกว่าการใช้ความเข้าใจรายการหรือ
อย่างไรก็ตามมีกรณีหนึ่งที่ปลอดภัยที่จะลบองค์ประกอบออกจากลำดับที่คุณกำลังวนซ้ำ: หากคุณลบเพียงหนึ่งรายการในขณะที่คุณกำลังวนซ้ำ นี้สามารถมั่นใจได้ใช้หรือreturn
break
ตัวอย่างเช่น:
for i, item in enumerate(lst):
if item % 4 == 0:
foo(item)
del lst[i]
break
การทำความเข้าใจนี้ง่ายกว่าการทำความเข้าใจกับรายการเมื่อคุณทำการดำเนินการบางอย่างโดยมีผลข้างเคียงของรายการแรกในรายการที่ตรงตามเงื่อนไขและจากนั้นนำรายการนั้นออกจากรายการทันที
ฉันสามารถคิดถึงวิธีการสามวิธีในการแก้ปัญหาของคุณ เป็นตัวอย่างที่ผมจะสร้างรายการแบบสุ่มของ somelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)]
tuples sum of elements of a tuple = 15
เงื่อนไขที่ว่าฉันเลือกเป็น ในรายการสุดท้ายเราจะมีสิ่งอันดับที่มีผลรวมไม่เท่ากับ 15
สิ่งที่ฉันเลือกเป็นตัวอย่างที่สุ่มเลือก รู้สึกอิสระที่จะเปลี่ยนรายการ tuplesและเงื่อนไขที่เราได้เลือก
วิธีที่ 1>ใช้เฟรมเวิร์กที่คุณแนะนำ (โดยที่หนึ่งเติมในโค้ดภายในสำหรับลูป) ฉันใช้รหัสขนาดเล็กด้วยdel
เพื่อลบสิ่งอันดับที่ตรงกับเงื่อนไขดังกล่าว อย่างไรก็ตามวิธีนี้จะพลาด tuple (ซึ่งเป็นไปตามเงื่อนไขที่กล่าวไว้) หากทั้งสอง tuples ที่วางอยู่ติดกันตรงตามเงื่อนไขที่กำหนด
for tup in somelist:
if ( sum(tup)==15 ):
del somelist[somelist.index(tup)]
print somelist
>>> [(1, 2, 3), (3, 6, 6), (7, 8, 9), (10, 11, 12)]
วิธีที่ 2>สร้างรายการใหม่ซึ่งมีองค์ประกอบ (tuples) โดยที่เงื่อนไขไม่ได้รับ (นี่เป็นสิ่งเดียวกับการลบองค์ประกอบของรายการที่ตรงตามเงื่อนไขที่กำหนด) ต่อไปนี้เป็นรหัสสำหรับที่:
newlist1 = [somelist[tup] for tup in range(len(somelist)) if(sum(somelist[tup])!=15)]
print newlist1
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]
วิธีที่ 3>ค้นหาดัชนีที่ตรงตามเงื่อนไขที่กำหนดจากนั้นใช้องค์ประกอบลบ (tuples) ที่สอดคล้องกับดัชนีเหล่านั้น ต่อไปนี้เป็นรหัสสำหรับการที่
indices = [i for i in range(len(somelist)) if(sum(somelist[i])==15)]
newlist2 = [tup for j, tup in enumerate(somelist) if j not in indices]
print newlist2
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]
วิธีที่ 1 และวิธีที่ 2 จะเร็วกว่าวิธีที่ 3 วิธีที่ 2 และวิธีที่ 3 มีประสิทธิภาพมากกว่าวิธีที่ 1 ฉันชอบวิธีที่ 2 สำหรับตัวอย่างข้างต้นtime(method1) : time(method2) : time(method3) = 1 : 1 : 1.7
สำหรับสิ่งที่มีศักยภาพที่จะใหญ่จริงๆฉันใช้สิ่งต่อไปนี้
import numpy as np
orig_list = np.array([1, 2, 3, 4, 5, 100, 8, 13])
remove_me = [100, 1]
cleaned = np.delete(orig_list, remove_me)
print(cleaned)
ควรเร็วกว่าสิ่งอื่นใด
ในบางสถานการณ์ที่คุณทำมากกว่าการกรองรายการทีละรายการคุณต้องการให้การวนซ้ำเปลี่ยนไปในขณะที่วนซ้ำ
นี่คือตัวอย่างที่การคัดลอกรายการล่วงหน้าไม่ถูกต้องการวนซ้ำแบบย้อนกลับเป็นไปไม่ได้และความเข้าใจในรายการนั้นไม่ใช่ตัวเลือก
""" Sieve of Eratosthenes """
def generate_primes(n):
""" Generates all primes less than n. """
primes = list(range(2,n))
idx = 0
while idx < len(primes):
p = primes[idx]
for multiple in range(p+p, n, p):
try:
primes.remove(multiple)
except ValueError:
pass #EAFP
idx += 1
yield p
หากคุณจะใช้รายการใหม่ในภายหลังคุณสามารถตั้งค่า elem เป็น None และตัดสินในลูปภายหลังเช่นนี้
for i in li:
i = None
for elem in li:
if elem is None:
continue
ด้วยวิธีนี้คุณไม่จำเป็นต้องคัดลอกรายการและง่ายต่อการเข้าใจ
เพิ่มจำนวนรายการและคุณต้องการลบทั้งหมดที่หารด้วย 3 ไม่ได้
list_number =[i for i in range(100)]
ใช้list comprehension
นี้จะดูแลรายการใหม่และสร้างพื้นที่หน่วยความจำใหม่
new_list =[i for i in list_number if i%3!=0]
ใช้lambda filter
ฟังก์ชั่นนี้จะสร้างรายการใหม่ผลลัพธ์และใช้พื้นที่ memeory
new_list = list(filter(lambda x:x%3!=0, list_number))
โดยไม่ต้องใช้พื้นที่หน่วยความจำสำหรับรายการใหม่และแก้ไขรายการที่มีอยู่
for index, value in enumerate(list_number):
if list_number[index]%3==0:
list_number.remove(value)