จะลบรายการในรายการได้อย่างไร?


259

ฉันได้รับnew_tagจากช่องข้อความด้วยself.response.get("new_tag")และselected_tagsจากช่องทำเครื่องหมายด้วย

self.response.get_all("selected_tags")

ฉันรวมพวกเขาเช่นนี้:

tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

( f1.striplistเป็นฟังก์ชันที่ตัดช่องว่างสีขาวภายในสตริงในรายการ)

แต่ในกรณีที่tag_listว่างเปล่า (ไม่มีแท็กใหม่จะเข้ามา) แต่มีบางส่วนselected_tags, มีสตริงที่ว่างเปล่าnew_tag_list" "

ตัวอย่างเช่นจากlogging.info:

new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']

ฉันจะกำจัดสตริงว่างได้อย่างไร

หากมีสตริงว่างในรายการ:

>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']

แต่ถ้าไม่มีสตริงว่าง:

>>> s = [u'Hello', u'Cool', u'Glam']
>>> if s.index(""):
        i = s.index("")
        del s[i]
    else:
        print "new_tag_list has no empty string"

แต่นี่ให้:

Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    if new_tag_list.index(""):
        ValueError: list.index(x): x not in list

ทำไมสิ่งนี้ถึงเกิดขึ้นและฉันจะแก้ไขได้อย่างไร

คำตอบ:


718

1) สไตล์เกือบภาษาอังกฤษ:

ทดสอบสถานะโดยใช้inโอเปอเรเตอร์แล้วใช้removeวิธี

if thing in some_list: some_list.remove(thing)

removeวิธีการจะลบเฉพาะเกิดขึ้นครั้งแรกของthingเพื่อที่จะลบที่เกิดขึ้นทั้งหมดที่คุณสามารถใช้แทนwhileif

while thing in some_list: some_list.remove(thing)    
  • ง่ายพออาจเป็นทางเลือกของฉันสำหรับรายการเล็ก ๆ (ไม่สามารถต้านทานหนึ่งสมุทร)

2) เป็ดพิมพ์ , EAFPสไตล์:

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

try:
    some_list.remove(thing)
except ValueError:
    pass # or scream: thing not in some_list!
except AttributeError:
    call_security("some_list not quacking like a list!")

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

หากคุณคาดหวังสิ่งต่าง ๆ ที่เกิดขึ้น:

while True:
    try:
        some_list.remove(thing)
    except ValueError:
        break
  • verbose เล็กน้อยสำหรับกรณีการใช้งานเฉพาะนี้ แต่มีสำนวนใน Python มาก
  • สิ่งนี้ทำงานได้ดีกว่า # 1
  • PEP 463เสนอไวยากรณ์ที่สั้นกว่าสำหรับลอง / ยกเว้นการใช้งานง่าย ๆ ที่จะมีประโยชน์ที่นี่ แต่ก็ไม่ได้รับการอนุมัติ

อย่างไรก็ตามด้วยการกำจัด contextlib () contextmanager (แนะนำใน python 3.4) โค้ดด้านบนสามารถทำให้ง่ายขึ้นในเรื่องนี้:

with suppress(ValueError, AttributeError):
    some_list.remove(thing)

อีกครั้งหากคุณคาดหวังสิ่งต่าง ๆ ที่เกิดขึ้น:

with suppress(ValueError):
    while True:
        some_list.remove(thing)

3) สไตล์การทำงาน:

รอบปี 1993 มีงูหลามlambda, reduce(), filter()และmap()มารยาทของเสียงกระเพื่อมของแฮ็กเกอร์ที่พลาดพวกเขาและส่งแพทช์การทำงาน * คุณสามารถใช้filterเพื่อลบองค์ประกอบออกจากรายการ:

is_not_thing = lambda x: x is not thing
cleaned_list = filter(is_not_thing, some_list)

มีทางลัดที่อาจเป็นประโยชน์สำหรับกรณีของคุณ: หากคุณต้องการกรองรายการเปล่า (ในความเป็นจริงรายการที่bool(item) == FalseชอบNone, เป็นศูนย์, สตริงว่างหรือคอลเลกชันว่างอื่น ๆ ) คุณสามารถส่ง None เป็นอาร์กิวเมนต์แรก:

cleaned_list = filter(None, some_list)
  • [อัพเดต] : ใน Python 2.x filter(function, iterable)ใช้เพื่อเทียบเท่า[item for item in iterable if function(item)](หรือ[item for item in iterable if item]ถ้าอาร์กิวเมนต์แรกคือNone); ในหลาม 3.x (item for item in iterable if function(item))ก็คือตอนนี้เทียบเท่ากับ ความแตกต่างที่ลึกซึ้งเป็นตัวกรองที่ใช้ในการกลับรายการตอนนี้ก็ทำงานเหมือนการแสดงออกกำเนิด - นี่คือตกลงถ้าคุณเป็นเพียงการทำซ้ำมากกว่ารายการทำความสะอาดและทิ้งมัน แต่ถ้าคุณต้องการรายการจริงๆคุณต้องใส่filter()โทร กับตัวlist()สร้าง
  • * โครงสร้าง Lispy เหล่านี้ถือเป็นเอเลี่ยนตัวน้อยใน Python รอบปี 2005 กุยแม้กระทั่งการพูดคุยเกี่ยวกับการลดลงfilter - พร้อมกับสหายmapและreduce(พวกเขาจะไม่ได้หายไป แต่ยังreduceถูกย้ายเข้ามาอยู่functoolsโมดูลซึ่งมีค่าดูถ้าคุณชอบฟังก์ชั่นการสั่งซื้อสูง )

4) สไตล์คณิตศาสตร์:

comprehensions รายการกลายเป็นรูปแบบที่ต้องการสำหรับการจัดการรายชื่อในหลามตั้งแต่การแนะนำในรุ่น 2.0 โดยPEP 202 เหตุผลเบื้องหลังก็คือ comprehensions รายการให้เป็นวิธีที่รัดกุมมากขึ้นเพื่อสร้างรายการในสถานการณ์ที่map()และfilter()และ / หรือลูปซ้อนกันอยู่ในปัจจุบันจะถูกนำมาใช้

cleaned_list = [ x for x in some_list if x is not thing ]

การแสดงออกกำเนิดเป็นที่รู้จักในรุ่น 2.4 โดยPEP 289 นิพจน์ตัวสร้างจะดีกว่าสำหรับสถานการณ์ที่คุณไม่ต้องการ (หรือต้องการ) เพื่อสร้างรายการทั้งหมดในหน่วยความจำเช่นเมื่อคุณต้องการวนซ้ำองค์ประกอบหนึ่งครั้ง หากคุณทำซ้ำเฉพาะรายการคุณสามารถนึกถึงนิพจน์ตัวสร้างเป็นความเข้าใจเกี่ยวกับรายการแบบประเมินที่ขี้เกียจ :

for item in (x for x in some_list if x is not thing):
    do_your_thing_with(item)

หมายเหตุ

  1. คุณอาจต้องการใช้ตัวดำเนินการอสมการ!=แทนis not( ความแตกต่างสำคัญ )
  2. สำหรับนักวิจารณ์ของวิธีการที่แสดงสำเนารายการ: ตรงกันข้ามกับความเชื่อที่ได้รับความนิยมนิพจน์เครื่องกำเนิดไฟฟ้าไม่ได้มีประสิทธิภาพมากกว่าการทำความเข้าใจรายการ - โปรดใส่รายละเอียดก่อนบ่น

3
ฉันขอแนะนำให้ละเว้นการจัดการ AttributeError ใน (2) ได้ไหม มันเสียสมาธิและไม่ได้รับการจัดการในส่วนอื่น ๆ (หรือส่วนอื่น ๆ ของส่วนเดียวกัน) ยิ่งกว่านั้นบางคนอาจคัดลอกโค้ดนั้นโดยไม่ทราบว่าพวกเขากำลังปราบปรามข้อยกเว้นอย่างรุนแรง คำถามเดิมถือว่าเป็นรายการคำตอบก็ควรเช่นกัน
Jason R. Coombs

1
คำตอบที่ครอบคลุม Super! ยอดเยี่ยมที่ได้แบ่งออกเป็นส่วนต่าง ๆ ตาม "สไตล์" ขอบคุณ!
halloleo

อันไหนเร็วที่สุด?
Sheshank S.

12
try:
    s.remove("")
except ValueError:
    print "new_tag_list has no empty string"

โปรดทราบว่าสิ่งนี้จะลบสตริงว่างเปล่าหนึ่งอินสแตนซ์ออกจากรายการของคุณ (เช่นเดียวกับโค้ดของคุณ) รายการของคุณมีมากกว่าหนึ่งรายการได้หรือไม่?


5

หากindexไม่พบสตริงการค้นหาก็จะทำให้ValueErrorคุณเห็น จับ ValueError ทั้ง:

try:
    i = s.index("")
    del s[i]
except ValueError:
    print "new_tag_list has no empty string"

หรือใช้findซึ่งส่งคืน -1 ในกรณีนั้น

i = s.find("")
if i >= 0:
    del s[i]
else:
    print "new_tag_list has no empty string"

find () เป็นรายการคุณลักษณะหรือไม่ ฉันได้รับ:>>> s [u'Hello', u'Cool', u'Glam'] >>> i = s.find("") Traceback (most recent call last): File "<pyshell#42>", line 1, in <module> i = s.find("") AttributeError: 'list' object has no attribute 'find'
Zeynel

2
remove()แนวทางของ Pietscker ตรงไปตรงมามากขึ้น: มันแสดงให้เห็นโดยตรงว่าโค้ดนั้นควรทำอะไร (ไม่จำเป็นต้องมีดัชนีระดับกลางi)
Eric O Lebigot

1
@Zeynel ไม่มีก็ควรจะอยู่ในทุกหลามดูdocs.python.org/library/string.html#string.find แต่ดังที่ EOL ชี้ให้เห็นว่าการใช้การลบจะดีกว่า
phihag

4

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

หากคุณมีรายการที่มีขนาดใหญ่มากการลบออกจากส่วนท้ายของรายการหลีกเลี่ยงการใช้งานภายในของ CPython memmoveสำหรับสถานการณ์ที่คุณสามารถสั่งซื้อรายการได้อีกครั้ง มันทำให้มีกำไรจากผลการดำเนินงานจะลบออกจากส่วนท้ายของรายการเพราะมันจะไม่จำเป็นต้องmemmove ทุกรายการหลังจากที่หนึ่งเอาคุณ - กลับขั้นตอนหนึ่ง(1)
สำหรับการลบแบบครั้งเดียวความแตกต่างด้านประสิทธิภาพอาจเป็นที่ยอมรับ แต่ถ้าคุณมีรายการขนาดใหญ่และจำเป็นต้องลบรายการจำนวนมาก - คุณอาจสังเกตเห็นถึงประสิทธิภาพ

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

วิธีนี้สามารถใช้สำหรับการลบที่มีประสิทธิภาพมากขึ้น
ตราบใดที่การสั่งซื้อรายการใหม่เป็นที่ยอมรับ (2)

def remove_unordered(ls, item):
    i = ls.index(item)
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()

คุณอาจต้องการหลีกเลี่ยงการเกิดข้อผิดพลาดเมื่อitemไม่มีอยู่ในรายการ

def remove_unordered_test(ls, item):
    try:
        i = ls.index(item)
    except ValueError:
        return False
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()
    return True

  1. ขณะที่ฉันทดสอบสิ่งนี้กับ CPython มันค่อนข้างเป็นไปได้มากที่การใช้งาน Python อื่น ๆ ทั้งหมดจะใช้อาร์เรย์เพื่อจัดเก็บรายการภายใน ดังนั้นหากพวกเขาใช้โครงสร้างข้อมูลที่ซับซ้อนที่ออกแบบมาสำหรับการปรับขนาดรายการที่มีประสิทธิภาพพวกเขามีแนวโน้มที่จะมีลักษณะการทำงานเดียวกัน

วิธีง่ายๆในการทดสอบนี้เปรียบเทียบความแตกต่างของความเร็วจากการลบออกจากด้านหน้าของรายการด้วยการลบองค์ประกอบสุดท้าย:

python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'

ด้วย:

python -m timeit 'a = [0] * 100000' 'while a: a.pop()'

(ให้ลำดับความเร็วที่แตกต่างกันซึ่งตัวอย่างที่สองนั้นเร็วกว่าด้วย CPython และ PyPy)

  1. ในกรณีนี้คุณอาจพิจารณาใช้setโดยเฉพาะถ้ารายการไม่ได้หมายถึงการจัดเก็บรายการที่ซ้ำกัน
    ในทางปฏิบัติ setแต่คุณอาจจำเป็นต้องจัดเก็บข้อมูลที่ไม่แน่นอนซึ่งไม่สามารถเพิ่มไปยัง ตรวจสอบ btree's ว่าสามารถสั่งซื้อข้อมูลได้หรือไม่

3

Eek ไม่ทำอะไรที่ซับซ้อน :)

เพียงแค่filter()แท็กของคุณ bool()ส่งคืนFalseสำหรับสตริงว่างดังนั้นแทนที่จะ

new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

คุณควรเขียน

new_tag_list = filter(bool, f1.striplist(tag_string.split(",") + selected_tags))

หรือดีกว่าให้วางตรรกะนี้ไว้ข้างในstriplist()เพื่อไม่ให้ส่งคืนสตริงว่างตั้งแต่แรก


ขอบคุณ! ทุกคำตอบที่ดี แต่ฉันคิดว่าฉันจะใช้มัน นี่คือstriplistฟังก์ชั่นของฉันฉันจะรวมโซลูชันของคุณได้อย่างไร: def striplist (l): "" "ตัด whitespaces จากสตริงในรายการ l" "" return ([x.strip () สำหรับ x in l])
Zeynel

1
@ Zeynel: แน่นอน คุณจะสามารถใส่การทดสอบภายในเข้าใจรายการของคุณเช่นนี้[x.strip() for x in l if x.strip()]หรือใช้ ธ ในตัวmapและฟังก์ชั่นเช่นนี้filter หากคุณต้องการที่จะทดสอบออกประเมินในล่ามโต้ตอบ:filter(bool, map(str.strip, l)) filter(bool, map(str.strip, [' a', 'b ', ' c ', '', ' ']))
dfichter

ตัวกรองมีทางลัดสำหรับกรณีนี้ (ประเมินองค์ประกอบในบริบทบูลีน): การใช้Noneแทนboolอาร์กิวเมนต์แรกก็เพียงพอแล้ว
เปาโล Scardine

2

นี่เป็นอีกวิธีการหนึ่งซับที่จะโยนออกมี:

next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)

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

โดยทั่วไปเมื่อค้นหาโซลูชันแบบหนึ่งซับที่ไม่ส่งข้อยกเว้นถัดไป () เป็นวิธีที่จะไปเนื่องจากเป็นหนึ่งในฟังก์ชัน Python ไม่กี่ตัวที่สนับสนุนอาร์กิวเมนต์เริ่มต้น


1

สิ่งที่คุณต้องทำคือ

list = ["a", "b", "c"]
    try:
        list.remove("a")
    except:
        print("meow")

แต่วิธีการนั้นมีปัญหา คุณต้องใส่บางอย่างในสถานที่ยกเว้นดังนั้นฉันพบสิ่งนี้:

list = ["a", "b", "c"]
if "a" in str(list):
    list.remove("a")

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