環狀緩衝區、循環緩衝區或環形緩衝區是一種資料結構,它使用單一固定大小的緩衝區,就好像頭尾相連一樣。
環狀緩衝區一開始是空的,並有某個預先定義好的長度。 例如,這是一個有 7 個元素的緩衝區:
[ ][ ][ ][ ][ ][ ][ ]
假設把 1 寫入緩衝區的中間(在環狀緩衝區裡,實際的起始位置並不重要):
[ ][ ][ ][1][ ][ ][ ]
接著假設再加入兩個元素,也就是 2 和 3,它們會被附加在 1 之後:
[ ][ ][ ][1][2][3][ ]
如果接著從緩衝區移除兩個元素,被移除的會是緩衝區裡最舊的值。 在這個例子裡,被移除的兩個元素是 1 和 2,緩衝區裡只剩下一個 3:
[ ][ ][ ][ ][ ][3][ ]
如果緩衝區裡有 7 個元素,那它就完全滿了:
[5][6][7][8][9][3][4]
當緩衝區滿了,就會引發錯誤,提醒用戶端在有空位之前,後續的寫入都會被阻擋。
當緩衝區滿了,用戶端可以選擇用強制寫入來覆寫最舊的資料。 在這個例子裡,會再加入兩個元素,也就是 A 和 B,它們會覆寫掉 3 和 4:
[5][6][7][8][9][A][B]
3 和 4 已被 A 和 B 取代,於是 5 現在成了緩衝區裡最舊的資料。 最後,如果移除兩個元素,回傳的會是 5 和 6,於是緩衝區變成:
[ ][ ][7][8][9][A][B]
因為還有可用的空間,如果用戶端再次使用覆寫來存放 C 和 D,那麼會使用先前存放 5 和 6 的空間,而不是 7 和 8 的位置。 7 仍然是最舊的元素,而緩衝區又再度滿了。
[C][D][7][8][9][A][B]
有時候,你的程式碼必須同時自訂例外,並用raise引發例外。這麼做時,一定要附上有意義的錯誤訊息,指出錯誤的來源是什麼。這能讓程式碼更容易閱讀,對除錯也有很大的幫助。
自訂例外可以透過新的例外類別來建立(詳情請見 classes),這些類別通常是 Exception 的子類別。
如果你已經知道錯誤來源會是某種例外的衍生型別,可以選擇繼承 Exception 類別底下的其中一種 built in error types。引發錯誤時,你仍然應該附上有意義的訊息。
這個練習要求你建立兩個_自訂例外_。一個是在你的環狀緩衝區已滿時要引發/「拋出」的例外,另一個則用在它已空時。只有在你的自訂例外恰當、以raise引發這些例外,並且附上恰當的錯誤訊息時,測試才會通過。
要自訂 built-in exception,請建立一個繼承該例外的class。以訊息引發自訂例外時,請把訊息寫成exception型別的引數:
# subclassing the built-in BufferError to create BufferFullException
class BufferFullException(BufferError):
"""Exception raised when CircularBuffer is full.
message: explanation of the error.
"""
def __init__(self, message):
self.message = message
# raising a BufferFullException
raise BufferFullException("Circular buffer is full")