歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

Python Ring Buffer的實(shí)現(xiàn)

發(fā)布時(shí)間:2017-09-03 23:38  回復(fù):0  查看:2725   最后回復(fù):2017-09-03 23:38  

本文和大家分享的主要是pythonRingBuffer的實(shí)現(xiàn)相關(guān)內(nèi)容,一起來看看吧,希望對(duì)大家學(xué)習(xí)python 有所幫助。

  RingBuffer,或者說Circular Buffer,是一個(gè)長(zhǎng)度固定的緩沖區(qū),當(dāng)從一端插入元素超過指定的最大長(zhǎng)度時(shí),緩沖區(qū)另一端的元素會(huì)溢出。當(dāng)用戶僅對(duì)近期一定長(zhǎng)度的歷史記錄感興趣時(shí),這種數(shù)據(jù)結(jié)構(gòu)就特別有用。

  它有三個(gè)要素:

  * 緩沖區(qū)的長(zhǎng)度固定

  * 先進(jìn)先出

  * 當(dāng)往緩沖區(qū)中增加或者刪除元素時(shí),其它元素的位置保持不變。

  本文介紹了三種實(shí)現(xiàn)方法及各自的特點(diǎn)。

  # collections.dequeue實(shí)現(xiàn)

  RingBuffer的一個(gè)直觀實(shí)現(xiàn)是使用Pyhton collectionsdequeue(發(fā)音deck)

  ``Pythonimport collectionsimport time

  d = collections.deque(maxlen=1000)

  tm1 = time.time()for i in range(int(1e6)):

  d.append(i)

  print(list(d)[:10])

  d.clear()

  tm2 = time.time()print("{:.2f}seconds".format(tm2 - tm1))

  執(zhí)行結(jié)果如下:

  [999000, 999001, 999002, 999003, 999004, 999005, 999006, 999007, 999008, 999009]

  0.20seconds

  支持的操作有pop, append;以及從左端操作的popleft, appendleft。但沒有緩沖區(qū)需要的read操作。緩沖區(qū)read操作包括兩個(gè)動(dòng)作,一是從緩沖區(qū)中讀取數(shù)據(jù);二是在讀取后清除掉數(shù)據(jù)。我們可以通過list(d:dequeue)clear的組合來實(shí)現(xiàn)這一功能。

  # 一個(gè)有趣的自定義實(shí)現(xiàn)

  David Ascher[這篇文章](# Implementing a Ring Buffer [Book]?)里給出了一個(gè)自定義的實(shí)現(xiàn):

  ``pythonclass RingBuffer:

  """ class that implements a not-yet-full buffer """

  def __init__(self,size_max):

  self.max = size_max

  self.data = []

  class __Full:

  """ class that implements a full buffer """

  def append(self, x):

  """ Append an element overwriting the oldest one. """

  self.data[self.cur] = x

  self.cur = (self.cur+1) % self.max

  def get(self):

  """ return list of elements in correct order """

  ret = self.data[self.cur:]+self.data[:self.cur]

  self.data.clear()

  return ret

  def append(self,x):

  """append an element at the end of the buffer"""

  self.data.append(x)

  if len(self.data) == self.max: #line 21

  self.cur = 0

  # Permanently change self's class from non-full to full

  self.__class__ = self.__Full

  def get(self):

  """ Return a list of elements from the oldest to the newest. """

  return self.data

  x = RingBuffer(1000)

  import time

  tm1 = time.time()for i in range(int(1e6)):

  x.append(i)

  print(x.get()[:10])

  tm2 = time.time()

  print("{:.2f}seconds".format(tm2 - tm1))

  執(zhí)行結(jié)果如下:

  [999000, 999001, 999002, 999003, 999004, 999005, 999006, 999007, 999008, 999009]

  0.64seconds

  這個(gè)實(shí)現(xiàn)的特點(diǎn)是,使用了數(shù)組,而不是雙端隊(duì)列來作為基礎(chǔ)存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)。當(dāng)然由于Ring Buffer的訪問特性,我們也基本上只對(duì)隊(duì)頭、隊(duì)尾元素進(jìn)行操作,所以無論是使用數(shù)組還是雙端隊(duì)列,操作復(fù)雜度都是O(1)。

  另一個(gè)值得一提的點(diǎn)是,它使用了動(dòng)態(tài)改變對(duì)象_class_實(shí)現(xiàn)的方式,避免了不必要的判斷語句操作,即在創(chuàng)建之初,緩沖區(qū)未滿時(shí),每插入一個(gè)元素,都要在第19行處進(jìn)行一次判斷;而一旦緩沖區(qū)被充滿后,RingBuffer對(duì)象升級(jí)為__Full類,從而行為也從此改變--今后再向緩沖區(qū)添加新的元素時(shí),不再進(jìn)行條件語句判斷。

  盡管代碼做了足夠的優(yōu)化,python內(nèi)建dequeue的實(shí)現(xiàn)性能更高一些。

  # 使用C語言的實(shí)現(xiàn)-pyringbuf

  [sirlark]C語言實(shí)現(xiàn)了一個(gè)開源的[RingBuffer],可以通過pip來安裝使用。

  pip install pyringbuf

這個(gè)模塊提供了push, pop, write, read等函數(shù),使用示例如下:

  >>> from ringbuf import RingBuffer

  >>> R = RingBuffer(5)    #choose your buffer size

  >>> R.push("a")          #push a single character into the buffer

  >>> R.pop()              #pop a single character

  'a'

  >>> R.write("bcdef")     #fill buffer with many characters at once

  >>> R.read(4)            #read many characters at once

  'bcde'

  >>> R.read(1)

  'f'

  >>> R.read(1)            #returns an empty string if the buffer is empty

這個(gè)模塊以c語言源碼提供,使用前需要編譯。

 

來源: 黑斑馬筆記

 

您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?