Python - 'if foo in dict' ลอง 'ลอง: dict [foo]'


24

นี่เป็นคำถามที่น้อยกว่าเกี่ยวกับธรรมชาติของการพิมพ์เป็ด

ก่อนอื่น - เมื่อต้องรับมือกับ dicts โดยเฉพาะอย่างยิ่งเมื่อโครงสร้างของ dict นั้นสามารถคาดเดาได้อย่างเป็นธรรมและโดยทั่วไปแล้วคีย์ที่ได้รับจะไม่ปรากฏ แต่บางครั้งก็เป็นฉันแรกที่คิดว่าสองวิธี:

if myKey in dict:
    do_some_work(dict[myKey])
else:
    pass

และแน่นอนว่าแนวทางการให้อภัยกับการอนุญาตของ Ye Olde

try:
    do_some_work(dict[myKey])
except KeyError:
    pass

ในฐานะที่เป็นคนที่แต่งตัวประหลาดงูหลามชำนาญฉันรู้สึกเหมือนฉันเห็นหลังที่ต้องการเป็นจำนวนมากซึ่งจะให้ความรู้สึกที่ผมคิดว่าแปลกเพราะในเอกสารหลามtry/exceptsดูเหมือนจะเป็นที่ต้องการเมื่อมีข้อผิดพลาดที่เกิดขึ้นจริงเมื่อเทียบกับเอ่อ ... ตัวตนของความสำเร็จ?

หาก dict เป็นครั้งคราวไม่มีคีย์ใน myDict และเป็นที่ทราบกันว่ามันจะไม่มีคีย์นั้นอยู่เสมอลอง / ยกเว้นในกรณีที่ทำให้เข้าใจผิดบริบทหรือไม่ นี่ไม่ใช่ข้อผิดพลาดในการเขียนโปรแกรมมันเป็นเพียงข้อมูลจริง - dict นี้ไม่มีรหัสเฉพาะนั้น

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

try:
    foo += bar
except TypeError:
    pass
else:
    return some_more_work(foo)

ไม่ว่าจะนำไปสู่การกลืนข้อผิดพลาดแปลก ๆ ทุกชนิดที่อาจเป็นผลมาจากรหัสที่ไม่ดี? โค้ดด้านบนอาจป้องกันคุณไม่ให้เห็นว่าคุณกำลังพยายามเพิ่ม2 + {}และคุณอาจไม่เคยรู้เลยว่ารหัสบางส่วนของคุณผิดไปอย่างมาก ฉันไม่แนะนำให้เราตรวจสอบทุกประเภทนั่นคือสาเหตุที่เป็น Python ไม่ใช่ JavaScript - แต่ด้วยบริบทของการลอง / ยกเว้นอีกครั้งดูเหมือนว่าควรจะจับโปรแกรมที่ทำสิ่งที่ไม่ควรทำแทน ของการเปิดใช้งานเพื่อดำเนินการต่อ

ฉันรู้ว่าตัวอย่างข้างต้นเป็นสิ่งที่โต้แย้งว่าเป็นคนฟางและในความเป็นจริงแล้วเจตนาไม่ดี แต่ด้วยความเชื่อแบบ pythonic ของbetter to ask forgiveness than permissionฉันอดไม่ได้ที่จะรู้สึกว่ามันเป็นคำถามที่ว่าเส้นในทรายเป็นจริงระหว่างการใช้งานที่ถูกต้องของ if / else กับ try / ยกเว้นคือโดยเฉพาะอย่างยิ่งเมื่อคุณรู้ว่าจะคาดหวังอะไร ข้อมูลที่คุณทำงานด้วย

ฉันไม่ได้พูดถึงความกังวลเรื่องความเร็วหรือการปฏิบัติที่ดีที่สุดที่นี่ฉันแค่สับสนอย่างเงียบ ๆ โดยแผนภาพ Venn ที่รับรู้ของคดีที่ดูเหมือนว่ามันจะไปทางใดทางหนึ่ง แต่คนทำผิดด้านลอง / ยกเว้น เพราะ 'ใครบางคนบอกว่ามันเป็น Pythonic' ฉันได้ข้อสรุปที่ผิดเกี่ยวกับการใช้ไวยากรณ์นี้หรือไม่?

python 

ฉันยอมรับว่าแนวโน้มต่อการลองยกเว้นจะเสี่ยงต่อการซ่อนข้อผิดพลาด ความคิดเห็นของฉันคือโดยทั่วไปคุณควรหลีกเลี่ยงการลองยกเว้นเงื่อนไขที่คุณคาดว่าจะเห็น (แน่นอนว่ามีข้อยกเว้นบางประการที่ใช้บังคับกับกฎนี้)
Gordon Bean

คำตอบ:


26

ใช้วิธีการรับแทน:

some_dict.get(the_key, default_value)

... ที่default_valueเป็นค่าที่ส่งกลับถ้าไม่ได้อยู่ในthe_key some_dictถ้าคุณไม่ใช้default_value, Noneถูกส่งกลับถ้าที่สำคัญคือการขาดหายไป

โดยทั่วไปในหลามคนมักจะชอบลอง / ยกเว้นกว่าการตรวจสอบสิ่งที่แรก - ดูEAFPรายการในคำศัพท์ โปรดทราบว่าฟังก์ชั่น "ทดสอบการเป็นสมาชิก" จำนวนมากใช้ข้อยกเว้นเบื้องหลัง


ไม่ใช่ปัญหาเดียวกันนี้ใช่ไหม หากเราพยายามที่จะทำงานกับ a dictและทำงานตามสิ่งที่พบมันรู้สึกเหมือนif myDict.get(a_key) is not None: do_work(myDict[a_key])wordier และยังเป็นอีกตัวอย่างหนึ่งของการมองโดยปริยายก่อนที่จะกระโจน - บวกกับ IMHO ที่อ่านได้น้อยกว่าเล็กน้อย บางทีฉันอาจไม่เข้าใจdict.get(a_key, a_default)ในบริบทที่ถูกต้อง แต่ดูเหมือนว่ามันเป็นสารตั้งต้นในการเขียน 'not-a-switch-statement ถ้า / elses' หรือค่าเวทมนต์ ฉันชอบวิธีการนี้แม้ว่าฉันจะมองลึกลงไป

1
@Stick - คุณทำ: data = myDict.get(a_key, default)และทั้งสองdo_workจะทำสิ่งที่ถูกต้องเมื่อได้รับdefault(เช่น. หรือคุณทำ)None if data: do_work(data)การค้นหาเดียวเท่านั้นที่จำเป็นในกรณีใดกรณีหนึ่ง (อาจเป็นได้if data is not None: ...ทั้งสองกรณี แต่ยังคงมีการค้นหาเพียงครั้งเดียวแล้วตรรกะของคุณเองก็สามารถเข้าครอบครองได้)
detly

1
ดังนั้นฉันจึงตัดสินใจว่า dict.get () ช่วยฉันได้มากในโครงการปัจจุบันของฉัน มันลดการนับบรรทัดของฉันทำความสะอาดtry/except/elseขยะที่ดูแย่มากและได้ปรับปรุง IMHO โดยรวมให้อ่านโค้ดของฉันได้โดยรวม ฉันได้เรียนรู้บทเรียนที่มีค่าซึ่งฉันเคยได้ยินมาก่อน แต่ไม่สนใจที่จะใส่ใจ: "ถ้าคุณรู้สึกว่าคุณกำลังเขียนโค้ดมากเกินไปให้หยุดและดูคุณสมบัติภาษา" ขอบคุณ !!

@ ติด - ดีใจที่ได้ยิน :) ฉันชอบคำแนะนำนั้นฉันไม่เคยได้ยินมาก่อน
Detly

1
ในทางเทคนิคอันไหนเร็วที่สุด?
Olivier Pons

4

ไม่มีคีย์หรือกฎยกเว้นหรือไม่

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

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

รหัสในข้อยกเว้นไม่ควรเป็นส่วนหนึ่งของข้อบกพร่องปกติ (เช่นหากไม่มีรหัสให้เพิ่ม) - ควรจัดการข้อผิดพลาด


4
"จากมุมมองที่เป็นจริงการโยน / จับจะช้ากว่ามากแล้วตรวจสอบว่ากุญแจอยู่ในตาราง" หรือไม่ ไม่จำเป็นเสมอไป ล่าสุดฉันตรวจสอบการใช้งาน Python ที่พบบ่อยที่สุดทำtry/ catchรุ่นเร็วขึ้น
Detly

2
การขว้างปา / จับงูหลามไม่ได้เป็นเรื่องใหญ่ที่มีประสิทธิภาพในการจัดการจนถึงระดับที่มักจะใช้สำหรับการควบคุมการไหลในหลาม นอกจากนี้ยังมีความเป็นไปได้ในบางสถานการณ์ที่ dict นั้นสามารถแก้ไขได้ระหว่างการทดสอบและการเข้าถึง
whatsisname

@whatsisname - ฉันคิดว่าจะเพิ่มเข้าไปในคำตอบของฉัน แต่ TBH ถ้าคุณมีอันตรายจากการแข่งขันเช่นนั้นคุณมีปัญหาที่ใหญ่กว่ามากที่ EAFP vs LBYL: P
detly

1

ในจิตวิญญาณของการเป็น "pythonic" และโดยเฉพาะอย่างยิ่งการพิมพ์เป็ด - ลอง / ยกเว้นดูเหมือนจะเปราะบางสำหรับฉัน

สิ่งที่คุณต้องการจริงๆคือสิ่งที่เข้าถึงได้เหมือน dict แต่__getitem__สามารถเขียนทับสิ่งที่ไม่คาดหวังได้ ตัวอย่างเช่นการใช้defaultdictจากไลบรารีมาตรฐาน:

In [1]: from collections import defaultdict

In [2]: foo = defaultdict(int)

In [3]: foo['a'] = 1

In [4]: foo['b']  # Someone did access checking with a try/except exactly as you have in the question
Out[4]: 0

In [5]: 'a' in foo
Out[5]: True

In [6]: 'b' in foo  # We never set it, but now it exists!
Out[6]: True

In [7]: 'c' in foo
Out[7]: False

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

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

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

ใช้รุ่นแรก, if myKey in mydict.


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

  • สภาวะการแย่งชิงคือไฟล์อาจถูกลบ / ย้าย / ฯลฯ
  • คุณไม่มีสิทธิ์อ่านไฟล์
  • ไฟล์เสียหาย

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

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