簡體   English   中英

Python:用迭代器和生成器替換我的 while true 循環?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM