วิธีลบรายการออกจากรายการขณะทำซ้ำ


934

ฉันวนซ้ำรายการของ tuples ใน Python และพยายามที่จะลบออกหากพวกเขามีคุณสมบัติตรงตามเกณฑ์ที่กำหนด

for tup in somelist:
    if determine(tup):
         code_to_remove_tup

ฉันควรใช้code_to_remove_tupอะไรแทน ฉันไม่สามารถหาวิธีลบรายการในแบบนี้ได้


คำตอบส่วนใหญ่ในหน้านี้ไม่ได้อธิบายว่าทำไมการลบองค์ประกอบในขณะที่วนซ้ำในรายการจะให้ผลลัพธ์ที่แปลก แต่คำตอบที่ได้รับการยอมรับในคำถามนี้ก็เป็นเช่นนั้นและอาจเป็นสิ่งล่อลวงที่ดีกว่าสำหรับผู้เริ่มต้น
ggorlen

คำตอบ:


827

คุณสามารถใช้ 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:


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

20
เกิดอะไรขึ้นถ้ารายการของฉันใหญ่และไม่สามารถทำสำเนาได้
jpcgt

15
@jpcgt คุณควรใช้somelist[:] = (x for x in somelist if determine(x))สิ่งนี้เพื่อสร้างเครื่องมือสร้างที่ไม่สามารถสร้างสำเนาที่ไม่จำเป็น
Rostislav Kondratenko

8
@RostislavKondratenko: list_ass_slice()ฟังก์ชั่นที่ใช้somelist[:]=โทรPySequence_Fast()ภายใน ฟังก์ชั่นนี้จะส่งคืนรายการเช่น@Alex Martelli โซลูชั่นที่ใช้รายการแทนเครื่องกำเนิดไฟฟ้าอาจมีประสิทธิภาพมากขึ้น
jfs

6
คุณอยากอธิบายว่าอะไรคือความแตกต่างระหว่างการกำหนดรายการความเข้าใจให้กับรายการและการโคลนนิ่งรายการโปรด รายการดั้งเดิมsomelistจะถูกทำให้กลายพันธุ์ในทั้งสองวิธีหรือไม่
Bowen Liu

589

คำตอบที่แนะนำการเข้าใจความหมายของรายการนั้นถูกต้องเกือบทุกประการยกเว้นว่าพวกเขาจะสร้างรายการใหม่ที่สมบูรณ์และจากนั้นให้ชื่อเดิมเป็นชื่อเดิมเหมือนกับที่พวกเขาไม่ได้แก้ไขรายการเก่าในสถานที่ มันแตกต่างจากสิ่งที่คุณทำโดยการลบแบบเลือกเช่นเดียวกับในข้อเสนอแนะของ @ Lennart - เร็วกว่า แต่ถ้ารายชื่อของคุณเข้าถึงได้ผ่านการอ้างอิงหลายรายการความจริงที่ว่าคุณเพิ่งทำการกู้คืนข้อมูลอ้างอิงหนึ่งรายการและไม่แก้ไขวัตถุรายการ ตัวเองสามารถนำไปสู่ข้อบกพร่องที่ลึกซึ้งและหายนะ

โชคดีที่มันง่ายมากที่จะได้รับทั้งความเร็วของความเข้าใจในรายการและความหมายที่จำเป็นของการเปลี่ยนแปลงแบบแทนที่ - รหัสเพียง:

somelist[:] = [tup for tup in somelist if determine(tup)]

สังเกตความแตกต่างเล็กน้อยกับคำตอบอื่น ๆ : อันนี้ไม่ได้กำหนดให้กับ barename - มันกำหนดให้กับชิ้นส่วนที่เพิ่งจะเป็นรายการทั้งหมดดังนั้นแทนที่เนื้อหา รายการภายในวัตถุรายการ Python เดียวกันแทนที่จะเพียงแค่ทำการอ้างอิงหนึ่งครั้งใหม่ (จากรายการวัตถุก่อนหน้าไปยังวัตถุรายการใหม่) เช่นคำตอบอื่น ๆ


1
ฉันจะทำภารกิจที่ได้รับการหั่นเป็นชิ้นเดียวกันกับ dict ได้อย่างไร? ใน Python 2.6
PaulMcG

11
@Paul: เนื่องจาก dicts ไม่มีการเรียงลำดับชิ้นจึงไม่มีความหมายสำหรับ dicts หากต้องการที่จะเปลี่ยนเนื้อหาของบริการพจนานุกรมaโดยเนื้อหาของ Dict ใช้b a.clear(); a.update(b)
Sven Marnach

1
ทำไมสามารถ 'reseat' การอ้างอิงหนึ่งรายการโดยแทนที่สิ่งที่ตัวแปรอ้างถึงทำให้เกิดข้อบกพร่อง ดูเหมือนว่าจะเป็นปัญหาที่อาจเกิดขึ้นในแอปพลิเคชันแบบมัลติเธรดเท่านั้นไม่ใช่แบบเธรดเดียว
Derek Dahmer

59
@Derek 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ที่นี่) อ้างอิงถึงรายการใหม่
Steven T. Snyder

ในความเป็นจริงการใช้filterฟังก์ชั่นก็สร้างรายการใหม่ไม่ได้แก้ไของค์ประกอบในสถานที่ ... เท่านั้นolist[:] = [i for i in olist if not dislike(i)]
John Strood

302

คุณต้องคัดลอกรายการและวนซ้ำก่อนหรือการวนซ้ำจะล้มเหลวด้วยผลลัพธ์ที่ไม่คาดคิด

ตัวอย่าง (ขึ้นอยู่กับประเภทของรายการ):

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

13
@Zen เนื่องจากรายการที่สองทำซ้ำผ่านสำเนาของรายการ ดังนั้นเมื่อคุณแก้ไขรายการต้นฉบับคุณจะไม่แก้ไขสำเนาที่คุณทำซ้ำ
Lennart Regebro

3
จะมีอะไรดีไปกว่าการทำ somelist [:] เปรียบเทียบกับ list (somelist)?
Mariusz Jamro

3
list(somelist)จะแปลง iterable เป็นรายการ somelist[:]ทำสำเนาของวัตถุที่สนับสนุนการแบ่งส่วน ดังนั้นพวกเขาไม่จำเป็นต้องทำสิ่งเดียวกัน ในกรณีนี้ฉันต้องการทำสำเนาsomelistวัตถุดังนั้นฉันจึงใช้[:]
Lennart Regebro

33
หมายเหตุสำหรับทุกคนที่อ่านข้อความนี้จะช้ามากสำหรับรายการ remove()ต้องผ่านรายการทั้งหมดสำหรับการวนซ้ำทุกครั้งดังนั้นจะใช้เวลาตลอดไป
vitiral

7
เวลา Big O ไม่สำคัญเมื่อจัดการกับรายการของรายการเพียงโหลเท่านั้น บ่อยครั้งที่ชัดเจนและเรียบง่ายสำหรับโปรแกรมเมอร์ในอนาคตที่จะเข้าใจมีคุณค่ามากกว่าประสิทธิภาพ
Steve

127
for i in range(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

คุณต้องย้อนกลับไปไม่เช่นนั้นมันก็เหมือนกับการตัดกิ่งไม้ที่คุณกำลังนั่งอยู่ :-)

ผู้ใช้ Python 2: แทนที่rangeด้วยxrangeเพื่อหลีกเลี่ยงการสร้างรายการฮาร์ดโค้ด


13
ใน Python เวอร์ชันล่าสุดคุณสามารถทำได้อย่างหมดจดยิ่งขึ้นโดยใช้reversed()builtin
ncoghlan

16
reverseed () ไม่สร้างรายการใหม่มันสร้าง reverse iterator ตามลำดับที่ให้มา เช่น enumerate () คุณต้องปิดมันใน list () เพื่อดึงรายการออกมาจริงๆ คุณอาจจะคิดว่าเรียง () ซึ่งจะสร้างรายการใหม่ทุกครั้ง (มันมีเพื่อให้มันสามารถจัดเรียงมัน)
ncoghlan

1
@Mauris เนื่องจากenumerateส่งคืนตัววนซ้ำและreversedคาดว่าจะเรียงลำดับ ฉันเดาว่าคุณสามารถทำได้reversed(list(enumerate(somelist)))ถ้าคุณไม่รังเกียจที่จะสร้างรายการพิเศษในความทรงจำ
drevicko

2
นี่คือ O (N * M) สำหรับอาร์เรย์มันช้ามากหากคุณลบหลายรายการออกจากรายการขนาดใหญ่ ดังนั้นไม่แนะนำ
Sam Watkins

2
@ SamWatkins ใช่คำตอบนี้มีไว้สำหรับเมื่อคุณลบองค์ประกอบสองสามรายการจากอาร์เรย์ขนาดใหญ่มาก ใช้หน่วยความจำน้อยลง แต่อาจmช้าลง
Navin

52

บทช่วยสอน 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 นี้จะสามารถปรับปรุงได้ เปรียบเทียบเช่นกับ:

  • Java ListIterator :: ลบเอกสารใด "การโทรนี้สามารถโทรหนึ่งครั้งต่อครั้งไปยังครั้งต่อไปหรือก่อนหน้า"
  • C ++ std::vector::eraseซึ่งส่งคืน interator ที่ถูกต้องไปยังองค์ประกอบหลังจากที่ลบออก

ทั้งสองอย่างทำให้ชัดเจนว่าคุณไม่สามารถแก้ไขรายการที่ถูกทำซ้ำยกเว้นตัววนซ้ำเองและให้วิธีที่มีประสิทธิภาพในการทำเช่นนั้นโดยไม่ต้องคัดลอกรายการ

บางทีอาจจะเป็นเหตุผลพื้นฐานที่รายการหลามจะถือว่าเป็นอาร์เรย์แบบไดนามิกได้รับการสนับสนุนและดังนั้นจึงประเภทของการกำจัดใด ๆ จะเป็นเวลาที่ไม่มีประสิทธิภาพ anyways ขณะที่ Java มีลำดับชั้นของอินเตอร์เฟซที่ดีกว่าที่มีทั้งArrayListและการใช้งานของLinkedListListIterator

ดูเหมือนจะไม่มีประเภทรายการเชื่อมโยงที่ชัดเจนใน Python stdlib เช่น: Python Linked List


48

แนวทางที่ดีที่สุดของคุณสำหรับตัวอย่างเช่นนั้นคือความเข้าใจในรายการ

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)

37

สำหรับผู้ที่ชอบการเขียนโปรแกรมการทำงาน:

somelist[:] = filter(lambda tup: not determine(tup), somelist)

หรือ

from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))

1. รายการความเข้าใจและการแสดงออกของตัวกำเนิดถูกยืมมาจาก Haskell ซึ่งเป็นภาษาที่ใช้งานได้จริง พวกมันใช้งานได้filterดีและ Pythonic มากกว่า 2. หากคุณต้องการlambdaใช้งานmapหรือfilterคอมพ์ list หรือ genexpr เป็นตัวเลือกที่ดีกว่าเสมอ mapและfilterอาจเร็วขึ้นเล็กน้อยเมื่อฟังก์ชันการแปลง / เพรดิเคตเป็น Python ในตัวที่ติดตั้งใน C และการทำซ้ำไม่ได้มีขนาดเล็ก แต่มักจะช้ากว่าเสมอเมื่อคุณต้องการlambdalistcomp / genexpr ที่สามารถหลีกเลี่ยงได้
ShadowRanger

13

ฉันต้องทำสิ่งนี้ด้วยรายการที่มีขนาดใหญ่และการทำซ้ำรายการดูเหมือนจะแพงโดยเฉพาะอย่างยิ่งเนื่องจากในกรณีของฉันจำนวนการลบจะน้อยเมื่อเทียบกับรายการที่เหลืออยู่ ฉันใช้วิธีการระดับต่ำนี้

array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
    if someTest(array[i]):
        del array[i]
        arraySize -= 1
    else:
        i += 1

สิ่งที่ฉันไม่ทราบก็คือการลบสองสามรายการที่มีประสิทธิภาพเมื่อเปรียบเทียบกับการคัดลอกรายการขนาดใหญ่ โปรดแสดงความคิดเห็นหากคุณมีข้อมูลเชิงลึกใด ๆ


ในกรณีของฉันฉันต้องการย้ายองค์ประกอบที่ไม่ต้องการเหล่านั้นไปยังรายการอื่น คุณมีความคิดเห็นใหม่เกี่ยวกับโซลูชันนี้หรือไม่? ฉันคิดว่ามันเป็นการดีที่จะใช้การลบบางอย่างแทนการทำซ้ำรายการ
gustavovelascoh

นี่เป็นคำตอบที่ถูกต้องหากประสิทธิภาพเป็นปัญหา (แม้ว่าจะเป็นเช่นเดียวกับ @Alexey) ที่กล่าวว่าการเลือกlistเป็นโครงสร้างข้อมูลในสถานที่แรกควรพิจารณาอย่างรอบคอบเนื่องจากการลบออกจากกลางรายการจะใช้เวลาเชิงเส้นตรงตามความยาวของรายการ หากคุณไม่จำเป็นต้องเข้าถึงไอเท็มตามลำดับแบบ k-th แบบสุ่มอาจพิจารณาได้OrderedDictหรือไม่
สูงสุด

@GVelascoh ทำไมไม่สร้างnewlist = []แล้วnewlist.append(array[i])ก็ก่อนdel array[i]?
สูงสุด

2
โปรดทราบว่านี่อาจไม่มีประสิทธิภาพเวลา: ถ้าlist()เป็นรายการที่เชื่อมโยงการเข้าถึงแบบสุ่มมีราคาแพงถ้าlist()เป็นอาร์เรย์การลบจะมีราคาแพงเนื่องจากพวกเขาต้องการย้ายองค์ประกอบต่อไปนี้ทั้งหมดไปข้างหน้า ตัวทำซ้ำที่เหมาะสมสามารถทำให้สิ่งต่าง ๆ ดีสำหรับการใช้งานรายการที่เชื่อมโยง อย่างไรก็ตามนี่อาจเป็นพื้นที่ที่มีประสิทธิภาพ
Ciro Santilli 冠状病毒审查六四事件法轮功

10

อาจเป็นเรื่องที่ฉลาดที่จะสร้างรายการใหม่ถ้ารายการในปัจจุบันตรงกับเกณฑ์ที่ต้องการ

ดังนั้น:

for item in originalList:
   if (item != badValue):
        newList.append(item)

และเพื่อหลีกเลี่ยงการโค้ดใหม่โครงการทั้งหมดด้วยชื่อรายการใหม่:

originalList[:] = newList

บันทึกจากเอกสาร Python:

copy.copy (x) ส่งคืนสำเนาตื้น ๆ ของ x

copy.deepcopy (x) ส่งคืนสำเนาลึกของ x


3
นี่เป็นการเพิ่มข้อมูลใหม่ที่ไม่ได้อยู่ในคำตอบที่ได้รับการยอมรับเมื่อเร็ว ๆ นี้
Mark Amery

2
มันง่ายและเป็นอีกวิธีหนึ่งในการดูปัญหา @MarkAmery มันลดน้อยลงสำหรับคนที่ไม่ชอบไวยากรณ์การบีบอัด
ntk4

9

แต่เดิมคำตอบนี้ถูกเขียนขึ้นเพื่อตอบคำถามที่ถูกทำเครื่องหมายว่าซ้ำกัน: การลบพิกัดออกจากรายการบนไพ ธ อน

รหัสของคุณมีสองปัญหา:

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' เมื่อรายการถูกส่งผ่านไปยังมัน สำหรับวัตถุประสงค์ในทางปฏิบัติคุณสามารถคิดว่าเป็นการสร้างสำเนาของอาร์กิวเมนต์ที่กลับรายการ นี่คือทางออกที่ฉันแนะนำ


4

หากคุณต้องการทำสิ่งอื่นระหว่างการทำซ้ำอาจเป็นการดีที่จะได้ทั้งดัชนี (ซึ่งรับประกันว่าคุณจะสามารถอ้างอิงได้ตัวอย่างเช่นถ้าคุณมีรายการ 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คือดัชนีที่คุณจะลบในภายหลังจะไม่เปลี่ยนแปลงในตัวคุณ


ทำไมการทำให้ดัชนีมีความเกี่ยวข้องมากขึ้นในกรณีที่คุณมีรายการ dicts มากกว่าในกรณีของรายการอื่น ๆ มันไม่สมเหตุสมผลเท่าที่ฉันจะบอกได้
Mark Amery


4

คำตอบส่วนใหญ่ที่นี่ต้องการให้คุณสร้างสำเนาของรายการ ฉันมีกรณีการใช้งานที่รายการค่อนข้างยาว (110K รายการ) และมันฉลาดกว่าที่จะลดรายการแทน

ก่อนอื่นคุณจะต้องเปลี่ยนห่วง foreach ด้วยในขณะที่วง ,

i = 0
while i < len(somelist):
    if determine(somelist[i]):
         del somelist[i]
    else:
        i += 1

ค่าของiจะไม่เปลี่ยนแปลงในบล็อก if เนื่องจากคุณต้องการรับค่าของรายการใหม่จากดัชนีเดียวกันเมื่อรายการเก่าถูกลบ


3

คุณสามารถลองวนกลับกันได้เพื่อ 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)))จะง่ายกว่าการคำนวณดัชนีตัวเอง
Mark Amery

@ MarkAmery ไม่คิดว่าคุณจะสามารถเปลี่ยนรายชื่อด้วยวิธีนี้
Queequeg

3

วิธีแก้ปัญหาที่เป็นไปได้วิธีหนึ่งที่มีประโยชน์หากคุณไม่เพียง แต่ต้องการลบบางสิ่ง แต่ยังทำบางสิ่งกับองค์ประกอบทั้งหมดในการวนรอบเดียว:

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

คุณควรใช้ความเข้าใจจริงๆ ง่ายต่อการเข้าใจ
Beefster

ถ้าฉันต้องการลบbadสิ่งต่าง ๆ ให้ทำอะไรกับมันและทำบางอย่างกับgoodสิ่งต่าง ๆ ในวงเดียว
Alexey

1
ที่จริงแล้วฉันรู้ว่ามีความฉลาดในการที่คุณทำสำเนาของรายการด้วย open slice ( alist[:]) และเนื่องจากคุณอาจทำสิ่งที่แฟนซีมันมีกรณีการใช้งานจริง การแก้ไขที่ดีนั้นดี รับคะแนนโหวตของฉัน
Beefster

2

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

`` `

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

`` `


2

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
Mark Amery

@MarkAmery กรณีการใช้งานหลักสำหรับเมื่อนี่คือเมื่อพยายามที่จะตรวจสอบว่ารายการควรจะลบ (หรือเพิ่มหรือย้าย) ตามไม่เพียง แต่ในรายการ แต่ในสถานะของรายการอื่นในรายการหรือสถานะของรายการเป็น ทั้งหมด ยกตัวอย่างเช่นมันเป็นไปไม่ได้กับ comprehensions รายการที่จะเขียนสิ่งที่ต้องการ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)]
กำทอน

2

วิธีที่มีประสิทธิภาพมากที่สุดคือรายการเข้าใจหลายคนแสดงกรณีของพวกเขาแน่นอนก็ยังเป็นวิธีที่ดีที่จะได้รับผ่านiteratorfilter

Filterรับฟังก์ชั่นและลำดับ Filterใช้ฟังก์ชั่นการส่งผ่านไปแต่ละองค์ประกอบในทางกลับกันแล้วตัดสินใจว่าจะเก็บหรือทิ้งองค์ประกอบขึ้นอยู่กับว่าค่าตอบแทนการทำงานเป็นหรือTrueFalse

มีตัวอย่าง (รับอัตราต่อรองใน tuple):

list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))  
# result: [1, 5, 9, 15]

ข้อควรระวัง: คุณไม่สามารถจัดการตัววนซ้ำได้ บางครั้งการวนซ้ำนั้นดีกว่าลำดับ


2

สำหรับ 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 ฯลฯ


สิ่งนี้ช่วยให้ฉันเข้าใจว่าทำไมรหัสของฉันจึงล้มเหลว
Wahid Sadik

2

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

ตัวอย่าง:

i = 0
length = len(list1)

while i < length:
    if condition:
        list1.remove(list1[i])
        i -= 1
        length -= 1

    i += 1

1

คำตอบอื่น ๆ นั้นถูกต้องว่าโดยปกติแล้วมันเป็นความคิดที่ดีที่จะลบออกจากรายการที่คุณกำลังวนซ้ำ ทำซ้ำย้อนกลับเพื่อหลีกเลี่ยงข้อผิดพลาด แต่ก็ยากที่จะทำตามรหัสที่ทำเช่นนั้นดังนั้นโดยทั่วไปแล้วคุณจะดีกว่าเมื่อใช้รายการความเข้าใจหรือfilterแต่มันเป็นเรื่องยากมากที่จะปฏิบัติตามรหัสที่ไม่ว่าดังนั้นคุณมักจะดีกว่าการใช้ความเข้าใจรายการหรือ

อย่างไรก็ตามมีกรณีหนึ่งที่ปลอดภัยที่จะลบองค์ประกอบออกจากลำดับที่คุณกำลังวนซ้ำ: หากคุณลบเพียงหนึ่งรายการในขณะที่คุณกำลังวนซ้ำ นี้สามารถมั่นใจได้ใช้หรือreturn breakตัวอย่างเช่น:

for i, item in enumerate(lst):
    if item % 4 == 0:
        foo(item)
        del lst[i]
        break

การทำความเข้าใจนี้ง่ายกว่าการทำความเข้าใจกับรายการเมื่อคุณทำการดำเนินการบางอย่างโดยมีผลข้างเคียงของรายการแรกในรายการที่ตรงตามเงื่อนไขและจากนั้นนำรายการนั้นออกจากรายการทันที


1

ฉันสามารถคิดถึงวิธีการสามวิธีในการแก้ปัญหาของคุณ เป็นตัวอย่างที่ผมจะสร้างรายการแบบสุ่มของ 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


0

สำหรับสิ่งที่มีศักยภาพที่จะใหญ่จริงๆฉันใช้สิ่งต่อไปนี้

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)

ควรเร็วกว่าสิ่งอื่นใด


จากสิ่งที่ฉันวัดได้ NumPy จะเริ่มเร็วขึ้นสำหรับรายการองค์ประกอบมากกว่า 20 รายการและเข้าถึงการกรองที่เร็วกว่า> 12x สำหรับรายการขนาดใหญ่ที่มีองค์ประกอบ 1000 รายการและอีกมากมาย
จอร์จ

0

ในบางสถานการณ์ที่คุณทำมากกว่าการกรองรายการทีละรายการคุณต้องการให้การวนซ้ำเปลี่ยนไปในขณะที่วนซ้ำ

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

""" 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

0

หากคุณจะใช้รายการใหม่ในภายหลังคุณสามารถตั้งค่า elem เป็น None และตัดสินในลูปภายหลังเช่นนี้

for i in li:
    i = None

for elem in li:
    if elem is None:
        continue

ด้วยวิธีนี้คุณไม่จำเป็นต้องคัดลอกรายการและง่ายต่อการเข้าใจ


-1

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