แนวทางปฏิบัติที่ดีที่สุดสำหรับ Python ยืนยัน


483
  1. มีปัญหาด้านประสิทธิภาพหรือการบำรุงรักษารหัสเมื่อใช้assertเป็นส่วนหนึ่งของรหัสมาตรฐานแทนที่จะใช้เพื่อการดีบักหรือไม่

    คือ

    assert x >= 0, 'x is less than zero'

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

    if x < 0:
        raise Exception, 'x is less than zero'
  2. นอกจากนี้ยังมีวิธีการตั้งกฎทางธุรกิจเช่นif x < 0 raise errorนั้นที่จะถูกตรวจสอบโดยไม่ต้องทำtry/except/finallyเช่นนั้นตลอดเวลาหากรหัสตลอดเวลาxน้อยกว่า 0 ข้อผิดพลาดจะเพิ่มขึ้นเช่นถ้าคุณตั้งไว้assert x < 0ที่จุดเริ่มต้นของฟังก์ชั่น ที่ไหนxจะกลายเป็น 0 แล้วมีข้อยกเว้นยก



29
-O และ -OO python พารามิเตอร์จะตัดการยืนยันของคุณออกไป นั่นควรผลักดันให้คุณคิดในสิ่งที่ดี
Peter Lada

4
การเชื่อมโยง Thomasz Zielinski ของเสียได้ก็ตอนนี้: mail.python.org/pipermail/python-list/2013-November/660568.html ฉันค่อนข้างมั่นใจว่า pipermail มีฟังก์ชั่น ID ที่ไม่เสถียรฉันพบลิงค์อื่นจากภายใน pipermail เดียวกันซึ่งชี้ไปที่ URL เดียวกันโดยมีเจตนาเดียวกัน
quodlibetor

3
ในกรณีmail.python.org/pipermail/python-list/2013-November/660568.htmlย้ายอีกครั้งก็จะถูกเก็บไว้ที่archive.is/5GfiG ในเรื่องของการโพสต์คือ "เมื่อจะใช้ยืนยัน" และเป็นผู้โพสต์ที่ยอดเยี่ยม (บทความจริงๆ) assertเกี่ยวกับแนวทางปฏิบัติที่ดีที่สุดสำหรับงูหลาม
clacke

คำตอบ:


144

เพื่อให้สามารถโยนข้อผิดพลาดโดยอัตโนมัติเมื่อ x กลายเป็นน้อยกว่าศูนย์ตลอดทั้งฟังก์ชั่น คุณสามารถใช้อธิบายระดับ นี่คือตัวอย่าง:

class LessThanZeroException(Exception):
    pass

class variable(object):
    def __init__(self, value=0):
        self.__x = value

    def __set__(self, obj, value):
        if value < 0:
            raise LessThanZeroException('x is less than zero')

        self.__x  = value

    def __get__(self, obj, objType):
        return self.__x

class MyClass(object):
    x = variable()

>>> m = MyClass()
>>> m.x = 10
>>> m.x -= 20
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "my.py", line 7, in __set__
    raise LessThanZeroException('x is less than zero')
LessThanZeroException: x is less than zero

10
แม้ว่าคุณสมบัติจะถูกนำไปใช้เป็นตัวอธิบาย แต่ฉันจะไม่เรียกตัวอย่างนี้ว่าใช้ นี้เป็นตัวอย่างของคุณสมบัติในตัวของตัวเอง: docs.python.org/library/functions.html#property
เจสันเบเกอร์

3
คุณสมบัติควรใช้ภายใน MyClass เมื่อตั้งค่า x โซลูชันนี้กว้างเกินไป

113
คำตอบที่ดีงามชอบ แต่ไม่ต้องทำอะไรกับคำถาม ... เราไม่สามารถทำเครื่องหมายคำตอบของ Deestan หรือ John Mee ว่าเป็นคำตอบที่ถูกต้องหรือไม่?
Vajk Hermecz

4
ดูเหมือนจะไม่ตอบคำถามของคำถาม นอกจากนี้นี่เป็นทางเลือกที่ไม่ดีสำหรับคุณสมบัติของคลาส Python
Dooms101

10
@VajkHermecz: อันที่จริงถ้าคุณอ่านคำถามนี้อีกสองคำถามในหนึ่งเดียว ผู้ที่ดูเฉพาะชื่อจะคุ้นเคยกับคำถามแรกเท่านั้นซึ่งคำตอบนี้ไม่ตอบ คำตอบนี้มีคำตอบสำหรับคำถามที่สอง
ArtOfWarfare

742

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

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


ตัวอย่างเช่นถ้าคุณกำลังเขียนฟังก์ชั่นในการอ่านจากแฟ้มการกำหนดค่าลงในdictการจัดรูปแบบที่ไม่เหมาะสมในไฟล์ควรยกConfigurationSyntaxErrorในขณะที่คุณสามารถที่คุณไม่ได้เกี่ยวกับการกลับมาassertNone


ในตัวอย่างของคุณถ้าxเป็นค่าที่ตั้งผ่านส่วนต่อประสานผู้ใช้หรือจากแหล่งภายนอกข้อยกเว้นจะดีที่สุด

หากxถูกกำหนดโดยรหัสของคุณเองในโปรแกรมเดียวกันให้ไปพร้อมกับการยืนยัน


126
นี่เป็นวิธีที่เหมาะสมในการใช้การยืนยัน ไม่ควรใช้เพื่อควบคุมโฟลว์โปรแกรม
Thane Brimhall

41
+1 สำหรับย่อหน้าสุดท้าย - แม้ว่าคุณควรอย่างชัดเจนพูดถึงว่าassertมีนัยif __debug__และอาจได้รับการปรับให้เหมาะสมออกไป - เป็นจอห์นหมี่คำตอบของรัฐ
โทเบียส KIENZLER

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

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

10
"การยืนยันควรใช้เพื่อทดสอบเงื่อนไขที่ไม่ควรเกิดขึ้น" ใช่. และความหมายของ "ควร" ที่สองคือ: หากสิ่งนี้เกิดขึ้นรหัสโปรแกรมไม่ถูกต้อง
Lutz Prechelt

362

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

ตัวสร้างโค้ดปัจจุบันไม่ปล่อยโค้ดสำหรับคำสั่ง assert เมื่อมีการร้องขอการปรับให้เหมาะสมในเวลารวบรวม - Python 2 Docs Python 3 Docs

หากคุณใช้assertในการใช้งานฟังก์ชั่นแอพพลิเคชั่นจากนั้นปรับการปรับใช้ให้เหมาะสมกับการใช้งานจริงคุณจะพบข้อผิดพลาด "but-it-work-in-dev"

ดูPYTHONOPTIMIZEและ-O -OO


26
ว้าว! สิ่งสำคัญที่ควรทราบคือ! ฉันวางแผนที่จะใช้ asserts เพื่อตรวจสอบบางสิ่งที่ไม่ควรพลาดซึ่งความล้มเหลวจะบ่งบอกว่ามีบางคนจัดการข้อมูลของพวกเขาอย่างระมัดระวังเพื่อพยายามเข้าถึงข้อมูลที่พวกเขาไม่ควรเข้าถึง มันไม่ได้ผล แต่ฉันต้องการปิดความพยายามของพวกเขาอย่างรวดเร็วด้วยการยืนยันดังนั้นการที่การเพิ่มประสิทธิภาพในการผลิตออกมานั้นจะเอาชนะเป้าหมายได้ ผมคิดว่าผมเพิ่งจะแทน โอ้ - ฉันเพิ่งค้นพบชื่อ aptly พร้อม subclasses ใน! ที่สมบูรณ์แบบ! raiseExceptionSuspiciousOperation ExceptionDjango
ArtOfWarfare

โดย @ArtOfWarfare ถ้าคุณใช้banditรหัสของคุณมันจะเตือนคุณเกี่ยวกับสิ่งนี้
Nagev

132

วัตถุประสงค์สี่ประการของ assert

สมมติว่าคุณทำงานกับโค้ด 200,000 บรรทัดกับเพื่อนร่วมงานสี่คน ได้แก่ Alice, Bernd, Carl และ Daphne พวกเขาเรียกรหัสของคุณคุณเรียกรหัสของพวกเขา

จากนั้นassertมีสี่บทบาท :

  1. แจ้ง Alice, Bernd, Carl และ Daphne ตามที่คุณคาดหวัง
    สมมติว่าคุณมีเมธอดที่ประมวลผลรายการของ tuples และตรรกะของโปรแกรมสามารถแตกถ้า tuples เหล่านั้นไม่เปลี่ยนรูปไม่ได้:

    def mymethod(listOfTuples):
        assert(all(type(tp)==tuple for tp in listOfTuples))

    นี่คือความน่าเชื่อถือมากกว่าข้อมูลที่เทียบเท่าในเอกสารประกอบและง่ายต่อการบำรุงรักษา

  2. แจ้งคอมพิวเตอร์ถึงสิ่งที่รหัสของคุณคาดหวัง
    assertบังคับใช้พฤติกรรมที่เหมาะสมจากผู้เรียกรหัสของคุณ หากรหัสของคุณเรียกรหัสของ Alices และ Bernd โทรหาคุณโดยที่ไม่มีassertถ้าโปรแกรมขัดข้องในรหัส Alices รหัส Bernd อาจถือว่าเป็นความผิดของ Alice, Alice ตรวจสอบและอาจถือว่าเป็นความผิดของคุณคุณตรวจสอบและแจ้งให้ Bernd ทราบ ของเขา สูญเสียงานจำนวนมาก
    ด้วยการยืนยันใครก็ตามที่โทรผิดพวกเขาจะสามารถเห็นได้อย่างรวดเร็วว่าเป็นความผิดของพวกเขาไม่ใช่ของคุณ Alice, Bernd และคุณทุกคนได้รับประโยชน์ ประหยัดเวลาได้อย่างมหาศาล

  3. แจ้งผู้อ่านรหัสของคุณ (รวมถึงตัวคุณเอง) ว่าโค้ดของคุณประสบความสำเร็จในบางจุด
    สมมติว่าคุณมีรายการของรายการและแต่ละรายการสามารถทำความสะอาด (ซึ่งดี) หรืออาจเป็น smorsh, trale, gullup หรือ twinkled (ซึ่งไม่เป็นที่ยอมรับ) ถ้ามันมีรอยเปื้อน ถ้ามันเป็นตอนที่มันจะต้องถูก baludoed; ถ้ามันเป็นนกนางนวลมันจะต้องถูกเจาะ (และอาจเป็นไปได้เช่นกัน); ถ้ามันเป็นทวีคูณมันจะต้องกระพริบอีกครั้งยกเว้นในวันพฤหัสบดี คุณได้รับความคิด: มันเป็นสิ่งที่ซับซ้อน แต่ผลลัพธ์สุดท้ายคือ (หรือควรเป็น) ว่ารายการทั้งหมดนั้นสะอาด สิ่งที่ถูกต้อง (TM) ที่ต้องทำคือการสรุปผลกระทบของการทำความสะอาดลูปของคุณดังนี้

    assert(all(entry.isClean() for entry in mylist))

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

  4. แจ้งคอมพิวเตอร์ว่าโค้ดของคุณประสบความสำเร็จในบางจุด
    หากคุณลืมที่จะก้าวเข้าสู่รายการที่ต้องการหลังจากวิ่งเหยาะๆมันassertจะช่วยให้วันของคุณและหลีกเลี่ยงการที่รหัสของคุณแบ่ง Daphne ที่รักในภายหลัง

ในใจของฉันassertจุดประสงค์สองประการของเอกสาร (1 และ 3) และการป้องกัน (2 และ 4) นั้นมีค่าเท่ากัน
แจ้งคนอาจจะมากขึ้นที่มีคุณค่ากว่าที่แจ้งคอมพิวเตอร์เพราะมันสามารถป้องกันไม่ให้เกิดความผิดพลาดมากassertมีจุดมุ่งหมายที่จะจับ (ในกรณีที่ 1) และความอุดมสมบูรณ์ของความผิดพลาดตามมาในกรณีใด ๆ


34
5. ยืนยัน isinstance ()ช่วย PyCharm (python IDE) เพื่อทราบชนิดของตัวแปรมันใช้สำหรับการเติมข้อความอัตโนมัติ
Cjkjvfnby

1
กำหนดสมมติฐานของรหัสเอกสารด้วยตนเองสำหรับสิ่งที่เป็นจริงในเวลาดำเนินการปัจจุบัน มันเป็นความคิดเห็นของสมมติฐานที่ได้รับการตรวจสอบ
pyj

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

9
@Cjkjvfnby ระวังเกี่ยวกับการใช้ isinstance () มากเกินไปตามที่อธิบายไว้ในรายการบล็อกนี้: " isinstance () ถือว่าเป็นอันตราย " ตอนนี้คุณสามารถใช้เอกสารเพื่อระบุประเภทใน Pycharm
binarysubstrate

2
การใช้การยืนยันในทางหนึ่งในการรับรองสัญญา ข้อมูลเพิ่มเติมเกี่ยวกับการออกแบบโดยสัญญาen.wikipedia.org/wiki/Design_by_contract
Leszek Zarna

22

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


3
ขวา. ดูเหมือนว่าโง่ที่จะตรวจสอบข้อผิดพลาดการยืนยันในผู้โทร
Raffi Khatchadourian

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

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

19

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

class XLessThanZeroException(Exception):
    pass

def CheckX(x):
    if x < 0:
        raise XLessThanZeroException()

def foo(x):
    CheckX(x)
    #do stuff here

ปัญหาอีกประการหนึ่งคือการใช้ assert สำหรับการตรวจสอบสภาพปกติคือทำให้ยากต่อการปิดการใช้งานการตรวจแก้จุดบกพร่องโดยใช้แฟล็ก -O


24
คุณสามารถต่อท้ายข้อความแสดงข้อผิดพลาดเพื่อยืนยัน มันเป็นพารามิเตอร์ที่สอง ที่จะทำให้มันเป็นคำอธิบาย
Raffi Khatchadourian

10

คำภาษาอังกฤษภาษายืนยันที่นี่จะใช้ในความหมายของการสาบาน , ยืนยัน , ปฏิญาณ มันไม่ได้หมายความว่า"ตรวจสอบ"หรือ"ควร" หมายความว่าคุณในฐานะผู้เขียนข้อความกำลังทำคำสั่งสาบานที่นี่:

# I solemnly swear that here I will tell the truth, the whole truth, 
# and nothing but the truth, under pains and penalties of perjury, so help me FSM
assert answer == 42

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


8

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

ตัวอย่างเช่นฟังก์ชั่นวนซ้ำ (2 ฟังก์ชันแยกต่างหากดังนั้น 1 จัดการข้อมูลเข้าที่ไม่ดีและอีกฟังก์ชั่นจัดการรหัสที่ไม่ดีทำให้ยากที่จะแยกแยะความแตกต่างกับการเรียกซ้ำ นี่จะทำให้ชัดเจนถ้าฉันลืมที่จะเขียนคำสั่ง if สิ่งที่ผิดพลาด

def SumToN(n):
    if n <= 0:
        raise ValueError, "N must be greater than or equal to 0"
    else:
        return RecursiveSum(n)

def RecursiveSum(n):
    #precondition: n >= 0
    assert(n >= 0)
    if n == 0:
        return 0
    return RecursiveSum(n - 1) + n
    #postcondition: returned sum of 1 to n

ค่าคงที่แบบวนซ้ำเหล่านี้มักจะแสดงด้วยการยืนยัน


2
วิธีนี้ทำได้ดีที่สุดกับนักตกแต่ง (@precondition และ @postcondition)
Caridorc

@Caridorc ประโยชน์ที่เป็นรูปธรรมของสิ่งนั้นคืออะไร?
Chiel ten Brinke

@ChieltenBrinke รหัสการจัดทำเอกสารด้วยตนเองแทน#precondition: n >= 0 และยืนยันเขาสามารถเขียนได้@precondition(lambda n: n >= 0)
Caridorc

@Caridorc มัณฑนากรเหล่านั้นเป็นผู้สร้างหรือไม่? และจะสร้างเอกสารจากวิธีการที่?
Chiel ten Brinke

@ChieltenBrinke ไม่ได้ในตัว แต่ง่ายต่อการใช้stackoverflow.com/questions/12151182/... สำหรับเอกสารเพียงแค่แก้ไข__doc__คุณลักษณะโดยการให้สตริงเพิ่มเติม
Caridorc

4

คือมีปัญหาประสิทธิภาพการทำงาน?

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

  • Be pragmatic :
    สมมติว่าคุณมีวิธีที่ประมวลผลรายการ tuples ที่ไม่ว่างเปล่าและตรรกะโปรแกรมจะแตกถ้า tuples เหล่านั้นไม่เปลี่ยนรูป คุณควรเขียน:

    def mymethod(listOfTuples):
        assert(all(type(tp)==tuple for tp in listOfTuples))

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

    def mymethod(listOfTuples):
        assert(type(listOfTuples[0])==tuple)  # in fact _all_ must be tuples!

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


2
assert(len(listOfTuples)==0 or type(listOfTyples[0])==tuple)ควรจะเป็น
osa

ไม่ควรทำ นั่นจะเป็นการทดสอบที่อ่อนแอกว่ามากเพราะมันจะไม่ตรวจสอบคุณสมบัติ 'ที่ไม่ว่างเปล่า' อีกต่อไปซึ่งการตรวจสอบที่สองที่ยืนยัน (ตัวแรกไม่ได้ถึงแม้ว่าควรจะเป็น)
Lutz Prechelt

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

3

นี่เป็นคำถามเปิดและฉันมีสองด้านที่ฉันต้องการสัมผัส: เมื่อเพิ่มการยืนยันและวิธีการเขียนข้อความแสดงข้อผิดพลาด

วัตถุประสงค์

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

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

สไตล์

ใน Python assertคำสั่งไม่ใช่หน้าที่! (จำassert(False, 'is true')ไม่ได้ว่าจะเพิ่ม แต่มีที่ออกจากทาง:

เมื่อใดและอย่างไรในการเขียน 'ข้อผิดพลาด' ที่เป็นตัวเลือก

นี้ acually นำไปใช้กับการทดสอบหน่วยกรอบซึ่งมักจะมีวิธีการเฉพาะมากมายที่จะทำยืนยัน ( assertTrue(condition), assertFalse(condition), assertEqual(actual, expected)ฯลฯ ) พวกเขามักจะให้วิธีการแสดงความคิดเห็นในการยืนยัน

ในรหัสทิ้งคุณสามารถทำได้โดยไม่มีข้อความแสดงข้อผิดพลาด

ในบางกรณีไม่มีอะไรที่จะเพิ่มในการยืนยัน:

def dump (บางอย่าง): ยืนยัน isinstance (บางอย่าง, แบบดัมพ์) # ...

แต่นอกเหนือจากนั้นข้อความจะมีประโยชน์สำหรับการสื่อสารกับโปรแกรมเมอร์อื่น ๆ (ซึ่งบางครั้งเป็นผู้ใช้แบบโต้ตอบของโค้ดของคุณเช่นใน Ipython / Jupyter เป็นต้น)

ให้ข้อมูลแก่พวกเขาไม่ใช่แค่เปิดเผยรายละเอียดการใช้งานภายใน

แทน:

assert meaningless_identifier <= MAGIC_NUMBER_XXX, 'meaningless_identifier is greater than MAGIC_NUMBER_XXX!!!'

เขียน:

assert meaningless_identifier > MAGIC_NUMBER_XXX, 'reactor temperature above critical threshold'

หรืออาจจะ:

assert meaningless_identifier > MAGIC_NUMBER_XXX, f'reactor temperature({meaningless_identifier }) above critical threshold ({MAGIC_NUMBER_XXX})'

ฉันรู้ฉันรู้ - นี่ไม่ใช่กรณีสำหรับการยืนยันแบบคงที่ แต่ฉันต้องการชี้ไปที่ค่าข้อมูลของข้อความ

ข้อความเชิงลบหรือเชิงบวก?

นี่อาจเป็นข้อถกเถียง แต่มันทำให้ฉันเจ็บปวดในการอ่านสิ่งต่าง ๆ เช่น:

assert a == b, 'a is not equal to b'
  • นี่เป็นสองสิ่งที่ขัดแย้งกันซึ่งเขียนถัดจากแต่ละคน ดังนั้นเมื่อใดก็ตามที่ฉันมีอิทธิพลต่อ codebase ฉันกดเพื่อระบุสิ่งที่เราต้องการโดยใช้คำกริยาพิเศษเช่น 'ต้อง' และ 'ควร' และไม่พูดในสิ่งที่เราไม่ต้องการ

    ยืนยัน a == b, 'a ต้องเท่ากับ b'

จากนั้นการAssertionError: a must be equal to bอ่านยังสามารถอ่านได้และคำสั่งจะดูเป็นตรรกะในโค้ด นอกจากนี้คุณยังสามารถนำบางสิ่งออกมาได้โดยไม่ต้องอ่านข้อมูลย้อนกลับ (ซึ่งบางครั้งอาจไม่มีให้ใช้)


1

ทั้งการใช้assertและการเพิ่มข้อยกเว้นเป็นเรื่องเกี่ยวกับการสื่อสาร

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

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

ปฏิบัติที่ดีที่สุด

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

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

มีปัญหาเรื่องประสิทธิภาพการทำงานเมื่อใช้งาน assert

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

มีปัญหาการบำรุงรักษารหัส [... ] กับการใช้งานหรือไม่ assert

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


1

การยืนยันคือการตรวจสอบ -
1. เงื่อนไขที่ถูกต้อง,
2. คำสั่งที่ถูกต้อง,
3. ตรรกะจริง;
ของรหัสที่มา แทนที่จะล้มเหลวในโครงการทั้งหมดมันให้สัญญาณเตือนว่ามีบางสิ่งที่ไม่เหมาะสมในไฟล์ต้นฉบับของคุณ

ในตัวอย่าง 1 เนื่องจากตัวแปร 'str' ไม่ใช่โมฆะ ดังนั้นจึงไม่มีการยืนยันหรือข้อยกเว้นใด ๆ

ตัวอย่างที่ 1:

#!/usr/bin/python

str = 'hello Python!'
strNull = 'string is Null'

if __debug__:
    if not str: raise AssertionError(strNull)
print str

if __debug__:
    print 'FileName '.ljust(30,'.'),(__name__)
    print 'FilePath '.ljust(30,'.'),(__file__)


------------------------------------------------------

Output:
hello Python!
FileName ..................... hello
FilePath ..................... C:/Python\hello.py

ในตัวอย่าง 2 var 'str' เป็นโมฆะ ดังนั้นเรากำลังบันทึกผู้ใช้จากการไปข้างหน้าของโปรแกรมที่ผิดพลาดโดยยืนยันคำสั่ง

ตัวอย่างที่ 2:

#!/usr/bin/python

str = ''
strNull = 'NULL String'

if __debug__:
    if not str: raise AssertionError(strNull)
print str

if __debug__:
    print 'FileName '.ljust(30,'.'),(__name__)
    print 'FilePath '.ljust(30,'.'),(__file__)


------------------------------------------------------

Output:
AssertionError: NULL String

ทันทีที่เราไม่ต้องการแก้ไขข้อบกพร่องและตระหนักถึงปัญหาการยืนยันในซอร์สโค้ด ปิดใช้งานการตั้งค่าสถานะการปรับให้เหมาะสม

หลาม -O assertStatement.py
ไม่มีอะไรจะได้รับการพิมพ์


0

ใน IDE เช่น PTVS, PyCharm, Wing assert isinstance()statement สามารถใช้เพื่อเปิดใช้งานการเติมโค้ดให้สมบูรณ์สำหรับวัตถุบางอย่างที่ไม่ชัดเจน


typing.castนี้ดูเหมือนว่าจะเกิดขึ้นก่อนการใช้งานของประเภทคำอธิบายประกอบหรือของ
คิวเมนตัส

-1

สำหรับสิ่งที่คุ้มค่าหากคุณกำลังจัดการกับรหัสที่ใช้assertงานได้อย่างถูกต้องการเพิ่มรหัสต่อไปนี้จะช่วยให้มั่นใจได้ว่ามีการเปิดใช้งานการยืนยัน:

try:
    assert False
    raise Exception('Python assertions are not working. This tool relies on Python assertions to do its job. Possible causes are running with the "-O" flag or running a precompiled (".pyo" or ".pyc") module.')
except AssertionError:
    pass

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