Python ให้อภัยเทียบกับการอนุญาตและการพิมพ์ดีดเป็ด


44

ใน Python ฉันมักจะได้ยินว่าเป็นการ "ให้อภัย" ดีกว่า (ยกเว้นการจับ) แทนที่จะดีกว่า "ขออนุญาต" (การตรวจสอบประเภท / เงื่อนไข) ในเรื่องที่เกี่ยวกับการบังคับใช้การพิมพ์เป็ดใน Python มันคือสิ่งนี้

try:
    x = foo.bar
except AttributeError:
    pass
else:
    do(x)

ดีกว่าหรือแย่กว่า

if hasattr(foo, "bar"):
    do(foo.bar)
else:
    pass

ในแง่ของประสิทธิภาพการอ่าน "pythonic" หรือปัจจัยสำคัญอื่น ๆ


17
มีตัวเลือกที่สามไม่ต้องทำอะไรและปฏิบัติต่อ foo ใด ๆ โดยไม่ต้องใช้แถบเป็นข้อผิดพลาด
jk

ฉันจำได้ว่าการได้ยินนั้นhasattrถูกนำไปใช้กับการลอง / จับที่แน่นอนนั้น ไม่แน่ใจว่ามันเป็นเรื่องจริงหรือเปล่า (มันจะทำหน้าที่แตกต่างกันในเรื่องของคุณสมบัติใช่มั้ยบางทีฉันอาจจะคิดgetattr.. )
Izkata

@Izkata: การใช้งานhasattrจะใช้ C-API ที่เทียบเท่าgetattr(กลับมาTrueหากประสบความสำเร็จFalseหากไม่ได้) แต่การจัดการข้อยกเว้นใน C นั้นเร็วกว่ามาก
Martijn Pieters

2
ฉันยอมรับคำตอบของ martijn แล้ว แต่ฉันต้องการเพิ่มว่าหากคุณพยายามกำหนดคุณลักษณะคุณควรลองใช้ try / catch เพราะอาจเป็นคุณสมบัติที่ไม่มี setter ซึ่งในกรณีนี้ hasattr จะเป็นจริง แต่จะยังคงเพิ่ม AttributeError
darkfeline

คำตอบ:


60

มันขึ้นอยู่กับว่าคุณคิดว่าการยกเว้นจะถูกโยนบ่อยเพียงใด

อย่างน้อยก็ในแง่ของความสามารถในการอ่านและ pythonic-ness แต่ถ้า 90% ของวัตถุของคุณไม่มีแอตทริบิวต์barคุณจะสังเกตเห็นความแตกต่างของประสิทธิภาพที่แตกต่างระหว่างสองแนวทางนี้:

>>> import timeit
>>> def askforgiveness(foo=object()):
...     try:
...         x = foo.bar
...     except AttributeError:
...         pass
... 
>>> def askpermission(foo=object()):
...     if hasattr(foo, 'bar'):
...         x = foo.bar
... 
>>> timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc')
2.9459929466247559
>>> timeit.timeit('testfunc()', 'from __main__ import askpermission as testfunc')
1.0396890640258789

แต่ถ้า 90% ของวัตถุของคุณจะมีแอตทริบิวต์ตารางที่ได้รับการเปิด:

>>> class Foo(object):
...     bar = None
... 
>>> foo = Foo()
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askforgiveness as testfunc, foo')
0.31336188316345215
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askpermission as testfunc, foo')
0.4864199161529541

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

ในที่สุดการใช้งานเชิงกลยุทธ์ของtimeitโมดูลอาจเป็นสิ่งที่ดีที่สุดที่คุณสามารถทำได้


1
เมื่อหลายสัปดาห์ก่อนฉันถามคำถามนี้: programmers.stackexchange.com/questions/161798/ …ที่ นั่นฉันถามว่าในภาษาที่พิมพ์ผิด ๆ คุณต้องทำงานตรวจสอบแบบพิเศษหรือไม่ รู้ว่าฉันเห็นคุณมี
Tulains Córdova

@ user1598390: เมื่อคุณกำหนด API ที่คาดว่าจะมีประเภทที่เป็นเนื้อเดียวกันคุณต้องทำการทดสอบบางอย่าง ส่วนใหญ่แล้วคุณทำไม่ได้ นี่เป็นพื้นที่เฉพาะที่คุณไม่สามารถหากฎเกี่ยวกับกระบวนทัศน์โดยรวมได้ฉันกลัว
Martijn Pieters

การพัฒนาระบบที่จริงจังเกี่ยวข้องกับการกำหนด API ดังนั้นฉันเดาว่าภาษาที่เข้มงวดจะดีที่สุดเพราะคุณต้องใช้รหัสน้อยลงเนื่องจากคอมไพเลอร์ตรวจสอบประเภทสำหรับคุณในเวลารวบรวม
Tulains Córdova

1
@GarethRees: มันสร้างรูปแบบสำหรับช่วงครึ่งหลังของคำตอบที่ฉันผ่านในการโต้แย้งไปยังฟังก์ชั่นภายใต้การทดสอบ
Martijn Pieters

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

11

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

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


3

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

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

นี่เป็นปัญหาสากล แต่อนุสัญญาด้านสิ่งแวดล้อมหรือภาษามีความเกี่ยวข้อง ตัวอย่างเช่นใน Python คุณสามารถใช้แอตทริบิวต์ที่คุณคาดหวังและปล่อยให้ AttributeError เข้าถึงผู้โทรได้


-1

ในแง่ของความถูกต้องฉันคิดว่าการจัดการข้อยกเว้นเป็นวิธีที่จะไป (บางครั้งฉันใช้ Hasattr () เข้าหาตัวเอง) ปัญหาพื้นฐานเกี่ยวกับการพึ่งพา hasattr () คือการเปลี่ยนการละเมิดสัญญารหัสเป็นความล้มเหลวเงียบ (นี่เป็นปัญหาใหญ่ใน JavaScript ซึ่งไม่ได้โยนในคุณสมบัติที่ไม่ได้มีอยู่)


3
คำตอบของคุณดูเหมือนจะไม่เพิ่มขึ้นเกินกว่าที่ผู้อื่นได้กล่าวไว้แล้ว ซึ่งแตกต่างจากไซต์อื่น ๆ โปรแกรมเมอร์คาดหวังคำตอบที่อธิบายสาเหตุที่อยู่เบื้องหลังคำตอบ คุณสัมผัสกับจุดที่ดีกับปัญหาความล้มเหลวเงียบ แต่ไม่ได้ให้ความยุติธรรมกับมัน การอ่านข้อมูลต่อไปนี้อาจช่วยได้: programmers.stackexchange.com/help/how-to-answer

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