[英]Python: iterables & generators to replace my while true loops?
讓我們從我的問題開始:你能寫出比下面的代碼更好的代碼嗎?
FRAME_DELIMITER = b'\x0a\x0b\x0c\x0d'
def get_data():
f = bytearray();
# detect frame delimiter
while True:
f += read_byte()
if f[-4:] == FRAME_DELIMITER:
start = len(f)-2
break
# read data until next frame delimiter
while True:
f += self._read_byte()
if f[-4:] == FRAME_DELIMITER:
return f[start:-2]
簡而言之,此代碼正在讀取數據流並返回整個幀。 每幀由 0x0a 0x0b 0x0c 分隔。read_byte function 讀取數據流上的一個字節(也許檢索 x 字節的緩沖區會很方便)。
我查看了 Python 文檔,嘗試以更 Python 的方式編寫此代碼(以及更好的性能?)。
我來到了生成器和迭代器。
我們可以想象創建一個像這樣的生成器:
def my_generator(self):
while True:
yield self._read_byte()
並玩弄像這樣的列表理解和迭代工具:
f = b''.join(itertools.takewhile(lambda c: c != b'\x03', self.my_generator()))
但實際上我被卡住了,因為我需要檢查分隔符模式而不僅僅是一個字符。 您能幫我指明正確的方向嗎……或者我上面的代碼可能正是我所需要的?!
謝謝!
在沒有state的情況下執行您要進行的測試是不切實際的,但您可以在生成器中隱藏 state!
您可以讓您的生成器讀取幀本身,假設分隔符是一個常量值(或者您傳入所需的分隔符)。 collections.deque
可以讓它輕松地僅保留最后四個字符的 state,因此它不僅僅是在 state 中隱藏大量數據存儲:
def read_until_delimiter(self):
# Note: If FRAME_DELIMITER is bytes, and _read_byte returns len 1 bytes objects
# rather than ints, you'll need to tweak the definition of frame_as_deque to make it store bytes
frame_as_deque = collections.deque(FRAME_DELIMITER)
window = collections.deque(maxlen=len(FRAME_DELIMITER))
while window != frame_as_deque:
byte = self._read_byte()
yield byte
window.append(byte) # Automatically ages off bytes to keep constant length after filling
現在你的來電者可以這樣做:
f = bytearray(self.read_until_delimiter())
# Or bytearray().join(self.read_until_delimiter()) if reading bytes objects, not ints
start = len(f) - 2
注意:我根據FRAME_DELIMITER
的長度定義了maxlen
; 您的分隔符結尾幾乎永遠不會通過,因為您切掉了最后四個字節,並將它們與僅包含三個字節的常量進行比較。
我認為通過說更好的代碼是不切片連接bytes
序列而不是智能generator
的代碼,並且只使用一個 while 循環:
# just to simulate your method
data = b'AA\x0a\x0b\x0cBBqfdqsfqsfqsvcwccvxcvvqsfq\x0a\x0b\x0cqsdfqs'
index = -1
def get_bytes():
# you used two method
# return read_byte() if count == 2 else self._read_byte()
global index
index += 1
return data[index:index + 1]
FRAME_DELIMITER = b'\x0a\x0b\x0c'
def get_data():
def update_last_delimiter(v):
""" update the delemeter with the last readed element"""
nonlocal last_four_byte
if len(last_four_byte) < len(FRAME_DELIMITER):
last_four_byte += v
else:
last_four_byte = last_four_byte[1:] + v
count = 2
last_four_byte = b''
while True:
# because you have two method to extract bytes
# replace get_bytes() by (read_byte() if count == 2 else self._read_byte())
update_last_delimiter(get_bytes())
# only yields items when the first DELIMITER IS FOUND
if count < 2:
yield last_four_byte[1:2]
if last_four_byte == FRAME_DELIMITER:
count -= 1
if not count:
break
else:
# when first DELIMITER is found we should yield the [-2] element
yield last_four_byte[1:2]
print(b''.join(get_data()))
# b'\x0b\x0cBBqfdqsfqsfqsvcwccvxcvvqsfq\n\x0b'
這里的關鍵是跟蹤最后的DELIMITER
字節
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.