วิธีที่ดีที่สุดในการจัดการ list.index (อาจไม่มีอยู่จริง) ใน python?


113

ฉันมีรหัสที่มีลักษณะดังนี้:

thing_index = thing_list.index(thing)
otherfunction(thing_list, thing_index)

โอเคมันง่ายมาก แต่คุณเข้าใจแล้ว ตอนนี้thingอาจจะไม่เป็นจริงในรายการซึ่งในกรณีนี้ผมต้องการที่จะผ่าน thing_index-1 ในภาษาอื่นนี่คือสิ่งที่คุณคาดหวังว่าindex()จะได้รับคืนหากไม่พบองค์ประกอบ ในความเป็นจริงมันพ่นไฟล์ValueError.

ฉันสามารถทำได้:

try:
    thing_index = thing_list.index(thing)
except ValueError:
    thing_index = -1
otherfunction(thing_list, thing_index)

แต่มันรู้สึกสกปรกและฉันไม่รู้ว่าValueErrorจะเลี้ยงดูด้วยเหตุผลอื่นได้ไหม ฉันคิดวิธีแก้ปัญหาต่อไปนี้ตามฟังก์ชั่นเครื่องกำเนิดไฟฟ้า แต่ดูเหมือนจะซับซ้อนเล็กน้อย:

thing_index = ( [(i for i in xrange(len(thing_list)) if thing_list[i]==thing)] or [-1] )[0]

มีวิธีที่สะอาดกว่าในการบรรลุสิ่งเดียวกันหรือไม่? สมมติว่ารายการไม่ได้เรียงลำดับ


4
"... ในกรณีนี้ฉันต้องการส่ง -1 เป็นthing_index" - นี่ไม่ใช่ Pythonic แน่นอน การส่งผ่านค่าโทเค็น (ที่ไม่มีความหมาย) ในกรณีที่การดำเนินการไม่ประสบความสำเร็จจะถูกขมวดคิ้วข้อยกเว้นเป็นวิธีที่ถูกต้องที่นี่ โดยเฉพาะอย่างยิ่งเนื่องจากthing_list[-1]เป็นนิพจน์ที่ถูกต้องซึ่งหมายถึงรายการสุดท้ายในรายการ
Tim Pietzcker

@jellybean: facepalm ... มองเห็น java coder: P
Draemon

4
@ ทิม: มีstr.findวิธีการที่ทำเช่นนั้นคือส่งกลับ-1เมื่อไม่พบเข็มในเรื่อง
SilentGhost

@ ทิมไม่มีจะดีกว่านี้แล้ว ... และสิ่งนี้จะคล้ายกับ dict [คีย์] vs dict.get [คีย์]
เดรมอน

@SilentGhost: อืมน่าสนใจ ฉันอาจต้องดูรายละเอียดเพิ่มเติมนี้ str.index()จะทำให้เกิดข้อยกเว้นหากไม่พบสตริงการค้นหา
Tim Pietzcker

คำตอบ:


66

ไม่มีอะไร "สกปรก" เกี่ยวกับการใช้ try-except clause นี่คือวิธี pythonic ValueErrorจะเพิ่มขึ้นโดย.indexวิธีการเท่านั้นเนื่องจากเป็นรหัสเดียวที่คุณมี!

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


29
แน่นอนว่ามีข้อยกเว้นสำหรับกรณีพิเศษและแทบจะไม่เป็นเช่นนั้น ฉันจะไม่มีปัญหาดังกล่าวหากข้อยกเว้นมีความเฉพาะเจาะจงมากกว่า ValueError
Draemon

1
ฉันรู้ว่ามันสามารถโยนได้จากวิธีนั้นเท่านั้น แต่รับประกันว่าจะถูกโยนด้วยเหตุผลนั้นหรือไม่? ไม่ใช่ว่าฉันคิดได้ว่าสาเหตุอื่นที่ดัชนีจะล้มเหลว.. แต่ก็ไม่มีข้อยกเว้นสำหรับสิ่งที่คุณอาจคิดไม่ถึง?
Draemon

4
ไม่{}.get(index, '')pythonic มากกว่านี้หรือ? ไม่ต้องพูดถึงสั้นกว่าอ่านง่ายกว่า
Esteban Küber

1
ฉันใช้ dict [คีย์] เมื่อฉันคาดว่าคีย์จะมีอยู่และ dict.get (คีย์) เมื่อฉันไม่แน่ใจและฉันกำลังมองหาสิ่งที่เทียบเท่าที่นี่ การกลับมาNoneแทนที่จะเป็น -1 จะดี แต่เมื่อคุณแสดงความคิดเห็นด้วยตัวเอง str.find () จะส่งกลับ -1 ดังนั้นทำไมจึงไม่ควรมี list.find () ที่ทำสิ่งเดียวกัน ฉันไม่ได้ซื้ออาร์กิวเมนต์ "pythonic"
Draemon

3
แต่ประเด็นก็คือวิธีแก้ปัญหา pythonic ส่วนใหญ่คือการใช้try / except เท่านั้นไม่ใช่ค่า -1 sentinel เลย IE otherfunctionคุณควรเขียน ในทางกลับกันถ้ามันไม่พัง ...
Andrew Jaffe

53
thing_index = thing_list.index(elem) if elem in thing_list else -1

หนึ่งบรรทัด เรียบง่าย ไม่มีข้อยกเว้น.


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

4
@Draemon: เห็นด้วย - ว่าจะทำ 2 รอบ - แต่ไม่น่าเป็นไปได้ว่าจากฐานรหัสพันบรรทัดอันนี้จะเป็นคอขวด :) เราสามารถเลือกใช้วิธีแก้ปัญหาที่จำเป็นforได้ตลอดเวลา
Emil Ivanov

with lambdindexOf = lambda item,list_ : list_.index(item) if item in list_ else -1 # OR None
Alaa Akiel

17

dictประเภทมีgetฟังก์ชั่นที่สำคัญถ้าไม่ได้อยู่ในพจนานุกรมอาร์กิวเมนต์ที่ 2 getคือค่าที่มันควรจะกลับ ในทำนองเดียวกันจะมีการsetdefaultส่งคืนค่าในdictif ที่มีคีย์อยู่มิฉะนั้นจะตั้งค่าตามพารามิเตอร์เริ่มต้นของคุณแล้วส่งกลับพารามิเตอร์เริ่มต้นของคุณ

คุณสามารถขยายlistประเภทเพื่อให้มีgetindexdefaultวิธีการ

class SuperDuperList(list):
    def getindexdefault(self, elem, default):
        try:
            thing_index = self.index(elem)
            return thing_index
        except ValueError:
            return default

ซึ่งสามารถใช้งานได้เช่น:

mylist = SuperDuperList([0,1,2])
index = mylist.getindexdefault( 'asdf', -1 )

6

ValueErrorมีอะไรผิดปกติกับรหัสของคุณที่ใช้คือ นี่เป็นอีกหนึ่งซับในหากคุณต้องการหลีกเลี่ยงข้อยกเว้น:

thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1)

งูหลาม 2.6 หรือเปล่า ฉันรู้ว่าฉันไม่ได้พูดถึงมัน แต่ฉันใช้ 2.5 นี่อาจเป็นสิ่งที่ฉันทำใน 2.6
Draemon

1
@Draemon: ใช่next()ฟังก์ชันมีอยู่ใน Python 2.6+ แต่ใช้งานง่ายสำหรับ 2.5 โปรดดูการใช้ฟังก์ชัน next () สำหรับ Python 2.5
jfs

4

ประเด็นนี้เป็นหนึ่งในปรัชญาภาษา ตัวอย่างเช่นใน Java มีประเพณีอยู่เสมอว่าข้อยกเว้นควรใช้ใน "สถานการณ์พิเศษ" นั่นคือเมื่อเกิดข้อผิดพลาดแทนที่จะใช้สำหรับการควบคุมโฟลว์ ในตอนแรกนี่เป็นเหตุผลด้านประสิทธิภาพเนื่องจากข้อยกเว้น Java ทำงานช้า แต่ตอนนี้กลายเป็นรูปแบบที่ยอมรับแล้ว

ในทางตรงกันข้าม Python มักใช้ข้อยกเว้นเพื่อบ่งชี้โฟลว์โปรแกรมปกติเช่นการเพิ่ม a ValueErrorตามที่เรากำลังพูดถึงที่นี่ ไม่มีอะไร "สกปรก" เกี่ยวกับเรื่องนี้ในรูปแบบ Python และมีอีกมากมายที่มาจากที่มา ตัวอย่างที่พบบ่อยกว่านั้นคือStopIterationข้อยกเว้นซึ่งเพิ่มขึ้นโดยวิธีการวนซ้ำnext()เพื่อส่งสัญญาณว่าไม่มีค่าเพิ่มเติม


ที่จริง JDK พ่นทางข้อยกเว้นการตรวจสอบมากเกินไปดังนั้นฉันไม่แน่ใจว่าปรัชญาถูกนำไปใช้จริง Java ฉันไม่มีปัญหาต่อStopIterationเนื่องจากมีการกำหนดไว้ชัดเจนว่าข้อยกเว้นหมายถึงอะไร ValueErrorเป็นเพียงเล็กน้อยทั่วไปเกินไป
Draemon

ฉันอ้างถึงแนวคิดที่ว่าไม่ควรใช้ข้อยกเว้นสำหรับการควบคุมโฟลว์ : c2.com/cgi/wiki?DontUseExceptionsForFlowControlจำนวนข้อยกเว้นที่ตรวจสอบไม่มากนักที่ Java มีการอภิปรายอื่น ๆ ทั้งหมด: mindview.net/Etc/Discussions / CheckedExceptions
Tendayi Mawushe

4

หากคุณทำสิ่งนี้บ่อยๆจะเป็นการดีกว่าที่จะเผามันทิ้งในฟังก์ชันตัวช่วย:

def index_of(val, in_list):
    try:
        return in_list.index(val)
    except ValueError:
        return -1 

4

อะไรประมาณนี้😃:

li = [1,2,3,4,5] # create list 

li = dict(zip(li,range(len(li)))) # convert List To Dict 
print( li ) # {1: 0, 2: 1, 3: 2, 4:3 , 5: 4}
li.get(20) # None 
li.get(1)  # 0 

1

แล้วสิ่งนี้:

otherfunction(thing_collection, thing)

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

if thing in thing_collection:
    ... proceed with operation on thing

ซึ่งจะใช้งานได้ถ้า thing_collection เป็นรายการทูเปิลชุดหรือคำสั่ง

สิ่งนี้อาจชัดเจนกว่า:

if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER:

ซึ่งเป็นรหัสที่คุณมีอยู่แล้วในฟังก์ชันอื่น ๆ



0

ฉันมีปัญหาเดียวกันกับเมธอด ".index ()" ในรายการ ฉันไม่มีปัญหากับความจริงที่ว่ามันมีข้อยกเว้น แต่ฉันไม่เห็นด้วยอย่างยิ่งกับข้อเท็จจริงที่ว่ามันเป็น ValueError ที่ไม่สามารถอธิบายได้ ฉันเข้าใจได้ว่ามันจะเป็น IndexError หรือไม่

ฉันเห็นว่าทำไมการส่งคืน "-1" จึงเป็นปัญหาเช่นกันเนื่องจากเป็นดัชนีที่ถูกต้องใน Python แต่ตามความเป็นจริงฉันไม่เคยคาดหวังว่าเมธอด ".index ()" จะส่งคืนจำนวนลบ

ต่อไปนี้เป็นหนึ่งซับ (ตกลงมันเป็นบรรทัดที่ค่อนข้างยาว ... ) ไปยังรายการหนึ่งครั้งและส่งกลับ "ไม่มี" หากไม่พบรายการ การเขียนใหม่ให้กลับ -1 เป็นเรื่องเล็กน้อยหากคุณต้องการ

indexOf = lambda list, thing: \
            reduce(lambda acc, (idx, elem): \
                   idx if (acc is None) and elem == thing else acc, list, None)

วิธีใช้:

>>> indexOf([1,2,3], 4)
>>>
>>> indexOf([1,2,3], 1)
0
>>>

-2

ฉันไม่รู้ว่าทำไมคุณควรคิดว่ามันสกปรก ... เพราะข้อยกเว้น? หากคุณต้องการ oneliner นี่คือ:

thing_index = thing_list.index(elem) if thing_list.count(elem) else -1

แต่ฉันจะไม่แนะนำให้ใช้มัน ฉันคิดว่าโซลูชันของ Ross Rogers นั้นดีที่สุดใช้วัตถุเพื่อห่อหุ้มพฤติกรรมที่คุณต้องการอย่าพยายามผลักดันภาษาให้ถึงขีด จำกัด โดยเสียค่าใช้จ่ายในการอ่าน


1
ใช่เนื่องจากข้อยกเว้น รหัสของคุณจะทำการค้นหาเชิงเส้นสองครั้งจะไม่? ไม่ใช่เรื่องประสิทธิภาพที่สำคัญที่นี่ โซลูชัน SuperDuperList นั้นดี แต่ดูเหมือนว่าจะ overkill ในสถานการณ์เฉพาะนี้ ฉันคิดว่าฉันจะพบข้อยกเว้น แต่ฉันต้องการดูว่ามีวิธีที่สะอาดกว่า (สำหรับความงามของฉัน) หรือไม่
Draemon

@Draemon: คุณจะห่อรหัสที่คุณมีไว้ในfind()ฟังก์ชันและมันจะสะอาดทั้งหมด;)
SilentGhost

1
อยากรู้จังว่าคำตอบของฉันมีสองโหวตลงในขณะที่ Emil Ivanov ของแม้ว่าจะมีความหมายเหมือนกัน แต่ก็เป็นหนึ่งในผู้โหวตที่โหวตมากที่สุด ส่วนใหญ่อาจนี้เกิดขึ้นเพราะฉันเป็นช้าลงเนื่องจากนับลูกจ้างฉัน () แทนของ "ใน" ผู้ประกอบ ... อย่างน้อยความคิดเห็นที่บอกว่าจะได้รับที่ดีแม้ว่า :-)
อลัน Franzoni

-2

ฉันขอแนะนำ:

if thing in thing_list:
  list_index = -1
else:
  list_index = thing_list.index(thing)

2
ปัญหาในการแก้ปัญหานี้คือ "-1" เป็นดัชนีที่ถูกต้องในรายการ (ดัชนีสุดท้ายตัวแรกจากท้าย) วิธีที่ดีกว่าในการจัดการสิ่งนี้คือการส่งคืน False ในสาขาแรกของเงื่อนไขของคุณ
FanaticD
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.