ฉันควรเพิ่มข้อยกเว้นใดในการรวมอาร์กิวเมนต์ที่ไม่ถูกต้อง / ผิดกฎหมายใน Python


544

ฉันสงสัยเกี่ยวกับวิธีปฏิบัติที่ดีที่สุดในการระบุการรวมอาร์กิวเมนต์ที่ไม่ถูกต้องใน Python ฉันเจอสถานการณ์บางอย่างที่คุณมีฟังก์ชั่นดังนี้

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

BadValueErrorแกล้งเฉพาะกับเรื่องนี้ก็คือแพคเกจทุกคนมีของตัวเองมักจะแตกต่างกันเล็กน้อย ฉันรู้ว่าใน Java มีอยู่java.lang.IllegalArgumentException- เป็นที่เข้าใจกันหรือไม่ว่าทุกคนจะสร้างของตัวเองBadValueErrorใน Python หรือมีวิธีอื่นที่ต้องการหรือไม่

คำตอบ:


608

ฉันแค่จะเพิ่มValueErrorเว้นแต่คุณจะต้องการข้อยกเว้นที่เฉพาะเจาะจงมากขึ้น ..

def import_to_orm(name, save=False, recurse=False):
    if recurse and not save:
        raise ValueError("save must be True if recurse is True")

ไม่มีจุดทำจริง ๆclass BadValueError(ValueError):pass- คลาสที่กำหนดเองของคุณเหมือนกันกับการใช้งานกับValueErrorดังนั้นทำไมไม่ใช้มัน


65
> "งั้นทำไมไม่ใช้ล่ะ" - ความจำเพาะ บางทีฉันต้องการจับที่ชั้นนอกบางอย่าง "MyValueError" แต่ไม่ใช่ "ValueError" ใด ๆ / ทั้งหมด
Kevin Little

7
ใช่แล้วส่วนหนึ่งของคำถามเรื่องความเฉพาะเจาะจงก็คือที่ซึ่งมีการเพิ่มค่าของ ValueError ถ้าฟังก์ชัน callee ชอบข้อโต้แย้งของคุณ แต่เรียก math.sqrt (-1) ภายในโทรอาจจะจับ ValueError คาดหวังว่ามันมีปากเสียงไม่เหมาะสม บางทีคุณอาจจะเพียงแค่ตรวจสอบข้อความในกรณีนี้ ...
cdleary

3
ฉันไม่แน่ใจว่ามีการโต้แย้ง: หากมีใครโทรmath.sqrt(-1)มานั่นเป็นข้อผิดพลาดในการเขียนโปรแกรมที่ต้องได้รับการแก้ไขต่อไป ไม่ได้มีวัตถุประสงค์ที่จะถูกจับในการทำงานของโปรแกรมปกติหรือมันจะเป็นผลมาจากValueError RuntimeError
ereOn

2
หากข้อผิดพลาดอยู่ที่ NUMBER ของอาร์กิวเมนต์สำหรับฟังก์ชันที่มีจำนวนตัวแปรของอาร์กิวเมนต์ ... ตัวอย่างเช่นฟังก์ชันที่อาร์กิวเมนต์ต้องมีจำนวนอาร์กิวเมนต์เท่ากันคุณควรเพิ่ม TypeError ให้สอดคล้องกัน และอย่าสร้างชั้นเรียนของคุณเองเว้นแต่ก) คุณมีกรณีการใช้งานหรือข) คุณกำลังส่งออกห้องสมุดเพื่อให้ผู้อื่นใช้งาน ฟังก์ชั่นก่อนวัยอันควรคือการตายของรหัส
Erik Aronesty

104

ฉันจะสืบทอดจาก ValueError

class IllegalArgumentError(ValueError):
    pass

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

หากคุณต้องการตรวจสอบข้อผิดพลาดนั้นจะมีประโยชน์หากมีชื่อ


26
หยุดเขียนชั้นเรียนและข้อยกเว้นที่กำหนดเอง - pyvideo.org/video/880/stop-writing-classes
Hamish Grubijan

40
@HamishGrubijan วิดีโอนั้นแย่มาก เมื่อมีคนแนะนำการใช้คลาสที่ดีเขาเพียงแค่พูดว่า "อย่าใช้คลาส" สุกใส เรียนดี แต่ไม่ได้ใช้คำของฉันมัน
Rob Grant

12
@ RobertGrant ไม่คุณไม่ได้รับมัน วิดีโอนั้นไม่ได้เกี่ยวกับตัวอักษร "อย่าใช้คลาส" มันไม่เกี่ยวกับสิ่งที่ซับซ้อนเกินไป
RayLuo

15
@ RayLuo คุณอาจมีสติตรวจสอบสิ่งที่วิดีโอพูดและแปลงให้เป็นข้อความทางเลือกที่น่าพึงพอใจ แต่นั่นคือสิ่งที่วิดีโอพูดและนั่นคือสิ่งที่ใครบางคนที่ไม่มีประสบการณ์มาก กับ
Rob Grant

3
@SamuelSantana อย่างที่ฉันพูดทุกครั้งที่ใครก็ตามยกมือขึ้นแล้วพูดว่า "แล้ว X ล่ะ" ที่ X เป็นความคิดที่ดีเขาแค่พูดว่า "อย่าสร้างคลาสอื่น" ค่อนข้างชัดเจน ฉันเห็นด้วยที่สำคัญคือความสมดุล; ปัญหาคือว่าคลุมเครือเกินไปที่จะใช้ชีวิตจริงโดย :-)
Rob Grant

18

ฉันคิดว่าวิธีที่ดีที่สุดในการจัดการสิ่งนี้คือวิธีที่ไพ ธ อนจัดการกับมัน Python เพิ่ม TypeError ตัวอย่างเช่น:

$ python -c 'print(sum())'
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: sum expected at least 1 arguments, got 0

นักพัฒนาซอฟต์แวร์ระดับสูงของเราเพิ่งพบหน้านี้ในการค้นหา google สำหรับ "ข้อผิดพลาดข้อผิดพลาดของ python" และฉันประหลาดใจที่คำตอบที่ชัดเจน (สำหรับฉัน) ไม่เคยแนะนำในทศวรรษนับตั้งแต่คำถามนี้ถูกถาม


8
ไม่มีอะไรที่ทำให้ฉันประหลาดใจ แต่ฉันเห็นด้วย 100% ว่า TypeError เป็นข้อยกเว้นที่ถูกต้องหากประเภทผิดในบางอาร์กิวเมนต์ที่ส่งผ่านไปยังฟังก์ชัน ValueError จะเหมาะสมถ้าตัวแปรเป็นประเภทที่ถูกต้อง แต่เนื้อหาและค่าไม่เหมาะสม
user3504575

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

2
ตามที่ @ user3504575 และ @Nobody กล่าวว่าจะใช้ TypeError หากอาร์กิวเมนต์ไม่ตรงกับฟังก์ชันของลายเซ็น (จำนวนอาร์กิวเมนต์ตำแหน่งที่ไม่ถูกต้องอาร์กิวเมนต์คำหลักที่มีชื่อผิดประเภทอาร์กิวเมนต์ที่ไม่ถูกต้อง) แต่จะใช้ ValueError เมื่อการเรียกฟังก์ชัน ตรงกับลายเซ็น แต่ค่าอาร์กิวเมนต์ไม่ถูกต้อง (เช่นการโทรint('a')) แหล่งที่มา
goodmami

ตามที่คำถามของ OP อ้างถึง "การรวมอาร์กิวเมนต์ที่ไม่ถูกต้อง" ดูเหมือนว่า TypeError น่าจะเหมาะสมเนื่องจากเป็นกรณีที่ลายเซ็นของฟังก์ชั่นนั้นผิดปกติสำหรับอาร์กิวเมนต์ที่ส่งผ่าน
J Bones

ตัวอย่างของคุณเรียกsum()โดยไม่มีข้อโต้แย้งซึ่งเป็น a TypeErrorแต่ OP เกี่ยวข้องกับการรวมกันของค่าอาร์กิวเมนต์ "ผิดกฎหมาย" เมื่อประเภทอาร์กิวเมนต์ถูกต้อง ในกรณีนี้ทั้งสองsaveและrecurseมี bools แต่ถ้าrecurseเป็นTrueแล้วไม่ควรจะเป็นsave นี่คือFalse ValueErrorฉันยอมรับว่าการแปลความหมายของชื่อคำถามจะได้รับคำตอบTypeErrorแต่ไม่ใช่สำหรับตัวอย่างที่นำเสนอ
goodmami


8

ขึ้นอยู่กับปัญหาที่เกิดขึ้นกับข้อโต้แย้ง

ถ้าอาร์กิวเมนต์มีชนิดไม่ถูกต้องยก TypeError ตัวอย่างเช่นเมื่อคุณได้รับสตริงแทนหนึ่งใน Booleans เหล่านั้น

if not isinstance(save, bool):
    raise TypeError(f"Argument save must be of type bool, not {type(save)}")

อย่างไรก็ตามโปรดทราบว่าใน Python เราไม่ค่อยทำการตรวจสอบเช่นนี้ หากการโต้แย้งนั้นไม่ถูกต้องจริง ๆ ฟังก์ชั่นที่ลึกกว่าอาจจะทำให้เราบ่น และถ้าเราแค่ตรวจสอบค่าบูลีนบางทีผู้ใช้รหัสบางคนอาจจะดึงมันมาเป็นสตริงโดยรู้ว่าสตริงที่ไม่ว่างนั้นเป็น True เสมอ มันอาจช่วยเขาหล่อ

หากอาร์กิวเมนต์มีค่าที่ไม่ถูกต้องให้เพิ่ม ValueError ดูเหมือนว่าจะเหมาะสมกว่าในกรณีของคุณ:

if recurse and not save:
    raise ValueError("If recurse is True, save should be True too")

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

if recurse and not save:
    logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
    save = True

ฉันคิดว่านี่เป็นคำตอบที่ถูกต้องที่สุด เห็นได้ชัดว่ามีการประเมินต่ำกว่ามาตรฐาน (7 โหวตจนถึงตอนนี้รวมถึงเหมืองด้วย)
งุดชิงโปง - อาสึกะเคนจิ -

-1

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

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

- เอกสารประกอบ ValueError


เปรียบเทียบgoogle.com/codesearch?q=lang:python+class \ + \ w ข้อผิดพลาด (([^ E] \ w * | E [^ x] \ w )): กับgoogle.com/codesearch?q=lang: python + class \ + \ w * ข้อผิดพลาด (ยกเว้น):
Markus Jarderot

13
การประกาศแจ้งนั้นหมายถึงว่าบิวด์อินจะเพิ่มค่าและไม่เพียงบิวด์อินเท่านั้นที่สามารถเพิ่มได้ มันจะไม่เหมาะสมในกรณีนี้สำหรับเอกสาร Python ที่จะพูดคุยเกี่ยวกับสิ่งที่ห้องสมุดภายนอกยก
Ignacio Vazquez-Abrams

5
ซอฟต์แวร์ Python ทุกชิ้นที่ฉันเคยเห็นมาใช้ValueErrorกับสิ่งของประเภทนี้ดังนั้นฉันคิดว่าคุณพยายามอ่านเอกสารมากเกินไป
James Bennett

6
หากเราจะใช้การค้นหารหัสของ Google เพื่อโต้แย้งสิ่งนี้: google.com/codesearch?q=lang%3Apython+raise%5C+ValueError # 66,300 รายในการเพิ่ม ValueError รวมถึง Zope, xen, Django, Mozilla (และ นั่นเป็นเพียงจากหน้าแรกของผลลัพธ์) หากมีข้อยกเว้นในตัวใช้งานได้พอดี ..
dbr

7
ตามที่ระบุไว้เอกสารไม่ชัดเจน มันควรจะถูกเขียนเป็น "ยกเมื่อการดำเนินการในตัวหรือฟังก์ชั่นในตัวรับ" หรือเป็น "ยกเมื่อฟังก์ชั่นหรือการดำเนินงานในตัวได้รับ" แน่นอนว่าไม่ว่าจะเป็นเจตนาดั้งเดิมหรือการปฏิบัติในปัจจุบันก็มีความกล้าหาญ (ตามที่ @dbr ชี้ให้เห็น) ดังนั้นจึงควรเขียนใหม่เป็นชุดที่สอง
บาร์

-1

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

class BadCallError(ValueError):
    pass

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

นี่ไม่ควรเป็นข้อยกเว้นมาตรฐานใน Python ใช่ไหม

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


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