วิธีแก้ไขรายการในระหว่างการวนซ้ำ


178

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


20
สตริงไม่ได้ทำให้ค่าที่ไม่แน่นอน
user470379

4
@ user470379: องค์ประกอบของรายการไม่สามารถเปลี่ยนแปลงได้หรือไม่ไม่เกี่ยวข้องกับว่าปลอดภัยหรือไม่ที่จะแก้ไขรายการที่อยู่ในขณะที่วนลูป
martineau

คำตอบ:


144

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

a = [1, 3, 5]
b = a
a[:] = [x + 2 for x in a]
print(b)

10
การกำหนดชิ้นงานนั้นฉลาดและหลีกเลี่ยงการปรับเปลี่ยนต้นฉบับระหว่างการวนซ้ำ แต่ต้องสร้างรายการชั่วคราวตามความยาวของต้นฉบับ
martineau

11
ทำไมเรากำหนด b = a
Vigrond

9
@Vigrond: ดังนั้นเมื่อprint bคำสั่งถูกดำเนินการคุณสามารถบอกได้ว่าaได้รับการแก้ไขในสถานที่แทนที่จะถูกแทนที่ ความเป็นไปได้อีกอย่างหนึ่งคือการprint b is aดูว่าพวกเขาทั้งคู่ยังคงอ้างถึงวัตถุเดียวกันหรือไม่
martineau

12
ทำไม [:] = และไม่ใช่แค่ a =?
kdubs

10
@kdubs: "... พร้อมการกำหนดชิ้นถ้าคุณต้องการเก็บการอ้างอิงที่มีอยู่ไว้ในรายการ"
Ignacio Vazquez-Abrams

163

เนื่องจากลูปด้านล่างปรับเปลี่ยนองค์ประกอบที่เห็นแล้วเท่านั้นจึงจะถือว่าเป็นที่ยอมรับได้:

a = ['a',' b', 'c ', ' d ']

for i, s in enumerate(a):
    a[i] = s.strip()

print(a) # -> ['a', 'b', 'c', 'd']

ซึ่งแตกต่างจาก:

a[:] = [s.strip() for s in a]

ตรงที่มันไม่ต้องการการสร้างรายการชั่วคราวและการกำหนดของมันเพื่อแทนที่ต้นฉบับแม้ว่ามันจะต้องมีการทำดัชนีมากขึ้น

ข้อควรระวัง:แม้ว่าคุณจะสามารถแก้ไขรายการได้ด้วยวิธีนี้ แต่คุณไม่สามารถเปลี่ยนจำนวนรายการได้listโดยไม่ต้องเสี่ยงกับโอกาสที่จะประสบปัญหา

นี่คือตัวอย่างของสิ่งที่ฉันหมายถึง - การลบรายการทำให้การจัดทำดัชนียุ่งเหยิงจากจุดนั้น:

b = ['a', ' b', 'c ', ' d ']

for i, s in enumerate(b):
    if s.strip() != b[i]:  # leading or trailing whitespace?
        del b[i]

print(b)  # -> ['a', 'c ']  # WRONG!

(ผลลัพธ์ไม่ถูกต้องเนื่องจากไม่ได้ลบรายการทั้งหมดที่ควรมี)

ปรับปรุง

เนื่องจากนี่เป็นคำตอบที่ได้รับความนิยมค่อนข้างต่อไปนี้เป็นวิธีการลบรายการ "ในสถานที่" อย่างมีประสิทธิภาพ (แม้ว่าจะไม่ใช่คำถาม)

b = ['a',' b', 'c ', ' d ']

b[:] = [entry for entry in b if entry.strip() == entry]

print(b)  # -> ['a']  # CORRECT

3
ทำไมไพ ธ อนจึงทำสำเนาองค์ประกอบแต่ละตัวในรูปแบบไวยากรณ์for i in aเท่านั้น นี่เป็นสิ่งที่ขัดกับความเป็นจริงมากดูเหมือนจะแตกต่างจากภาษาอื่นและทำให้เกิดข้อผิดพลาดในรหัสของฉันที่ฉันต้องแก้จุดบกพร่องเป็นเวลานาน Python Tutorial ไม่ได้พูดถึงมัน แม้ว่าจะต้องมีเหตุผลบางอย่างหรือไม่
xji

1
@JIXiang: มันไม่ได้ทำสำเนา มันเพียงกำหนดชื่อตัวแปรลูปให้กับองค์ประกอบหรือค่าที่ต่อเนื่องของสิ่งที่ทำซ้ำตาม
martineau

1
Eww เหตุใดจึงใช้ชื่อสองชื่อ ( a[i]และs) สำหรับวัตถุเดียวกันในบรรทัดเดียวกันเมื่อคุณไม่ต้องการ a[i] = a[i].strip()ฉันชอบที่จะทำ
Navin

3
@Navin: เพราะการa[i] = s.strip()ทำดัชนีเพียงครั้งเดียว
martineau

1
@martineau ไม่ดำเนินการจัดทำดัชนีในทุกย้ำและคุณกำลังทำอีกคนหนึ่งด้วยenumerate(b) a[i] =AFAIK เป็นไปไม่ได้ที่จะใช้ลูปนี้ใน Python ด้วยการดำเนินการสร้างดัชนีเพียง 1 ครั้งต่อการวนซ้ำของลูป :(
Navin

18

อีกหนึ่งสำหรับตัวแปรลูปดูดีกว่าฉันด้วย enumerate ():

for idx in range(len(list)):
    list[idx]=... # set a new value
    # some other code which doesn't let you use a list comprehension

19
หลายคนพิจารณาใช้สิ่งที่คล้ายกันrange(len(list))ใน Python และมีกลิ่นรหัส
martineau

2
@ Reishin: เนื่องจากenumerateเป็นตัวกำเนิดที่ไม่ได้สร้าง list.of tuples มันจะสร้างมันทีละตัวเมื่อมันวนซ้ำตามรายการ วิธีเดียวที่จะบอกได้ว่าอะไรจะช้ากว่าtimeitกัน
martineau

3
รหัส @martineau อาจไม่สวย แต่ตามtimeit enumerateช้ากว่า
Reishin

2
@Reishin: รหัสการเปรียบเทียบของคุณไม่ถูกต้องเนื่องจากไม่คำนึงถึงความจำเป็นในการดึงค่าในรายการที่ดัชนีที่กำหนดซึ่งไม่ได้แสดงในคำตอบนี้
martineau

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

11

การแก้ไขแต่ละองค์ประกอบในขณะที่วนรายการเป็นเรื่องปกติตราบใดที่คุณไม่เปลี่ยนองค์ประกอบเพิ่ม / ลบในรายการ

คุณสามารถใช้ list comprehension:

l = ['a', ' list', 'of ', ' string ']
l = [item.strip() for item in l]

หรือเพียงแค่ทำC-styleเพื่อห่วง:

for index, item in enumerate(l):
    l[index] = item.strip()

4

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

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

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


4

คุณสามารถทำสิ่งนี้:

a = [1,2,3,4,5]
b = [i**2 for i in a]

มันเรียกว่า list comprehension เพื่อให้คุณวนลูปในรายการได้ง่ายขึ้น


3

คำตอบที่ได้รับจาก Jemshit Iskenderov และ Ignacio Vazquez-Abrams นั้นดีมาก มันสามารถอธิบายเพิ่มเติมด้วยตัวอย่างนี้ลองจินตนาการว่า

a) รายการที่มีเวกเตอร์สองตัวมอบให้คุณ

b) คุณต้องการสำรวจรายการและย้อนกลับลำดับของแต่ละอาร์เรย์

สมมติว่าคุณมี

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
    i = i[::-1]   # this command does not reverse the string

print([v,b])

คุณจะได้รับ

[array([1, 2, 3, 4]), array([3, 4, 6])]

ในทางกลับกันถ้าคุณทำ

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
   i[:] = i[::-1]   # this command reverses the string

print([v,b])

ผลที่ได้คือ

[array([4, 3, 2, 1]), array([6, 4, 3])]

1

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

my_strings = ['a','b','c','d','e']
undesirable_strings = ['b','d']
for undesirable_string in undesirable_strings:
    for i in range(my_strings.count(undesirable_string)):
        my_strings.remove(undesirable_string)

ซึ่งเปลี่ยน my_strings เป็น ['a', 'c', 'e']


0

ในระยะสั้นที่จะทำการแก้ไขในรายการในขณะที่ซ้ำรายการเดียวกัน

list[:] = ["Modify the list" for each_element in list "Condition Check"]

ตัวอย่าง:

list[:] = [list.remove(each_element) for each_element in list if each_element in ["data1", "data2"]]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.