2016年8月24日 星期三

Python assert 斷言使用方式

使用 assert
所謂斷言(Assertion),指的是程式進行到某個時間點,斷定其必然是某種狀態,具體而言,也就是斷定該時間點上,某變數必然是某值,或某物件必具擁有何種特性值。

如果你在Python中要進行斷言測試,則可以使用assert陳述句:
assert <test>, <message>

test是狀態測試,而message是斷言失敗時所要呈現訊息。例如,在 定義類別 中定義的Account類別,存款的時候不能提負數,提款的時候也不能是負數(真正的提款機也不會有負數鍵),所以呼叫deposit()或withdraw()時傳入的值必然是大於0,這時你可以使用斷言檢查:

class Account:
    def __init__(self, number, name):
        self.number = number
        self.name = name
        self.balance = 0
       
    def deposit(self, amount):
        assert amount > 0, '必須是大於 0 的正數'
        self.balance += amount
       
    def withdraw(self, amount):
        assert amount > 0, '必須是大於 0 的正數'
        if amount <= self.balance:
            self.balance -= amount
        else:
            raise RuntimeError('balance not enough')

a = Account('E122', 'Justin')
a.deposit(-1)    # AssertionError: 必須是大於 0 的正數


類似的,在一個if判斷中,如果x大於0,就執行if區塊,否則x必須是等於0,這時也可使用斷言測試:
if x > 0:
    # do some
    ...
else:
    assert x == 0, 'x 此時一定要是 0,不會是負數'
    # do other
    ...

或者是物件某個方法執行過後,必然處於某個狀態,例如Stack的pop()執行過後,長度必然是少1:

class Stack:
    def __init__(self):
        self.idx = 0
        self.data = []
       
    def push(self, c):
        length = len(self.data)
        self.data.append(c)
        assert (length + 1) == len(self.data)
   
    def pop(self):
        length = len(self.data)
        ele = self.data.pop()
        assert (length - 1) == len(self.data)
        return ele


斷言是用來斷定程式某個時間點的狀態,最基本的原則是,斷言執行前後,不可以改變任何程式狀態,也就是不可以產生任何邊際效應。斷言會在最佳化時被省略,也就是最後編譯出來的程式碼,不會包括assert陳述句,這可以在使用python直譯器時,加上-O引數來達到,啟動Python直譯器時,若加上-O引數,則程式中__debug__會被設為False,也就不會包括或執行assert陳述句。例如:

print(__debug__)
assert False


上面這個程式,若執行python時加上-O引數,就不會引發AssertionError。由於assert有可能不會執行,所以這也是為何,你的斷言不得對程式狀態有任何改變。

所以,當你寫一個assert陳述時:
assert test, 'some message' # test 結果為 True 或 False

相當於在作以下的動作:
if __debug__:
    if not test:
        raise AssertionError('some message')

沒有留言:

張貼留言