有什么方法可以使发电机干燥吗?

Noc*_*wer 1 python iterator dry generator python-3.x

下面的方法在我的课堂上,尝试在完成工作之前准备好自身。底漆懒于完成其后续的处理循环。在这两个循环中重复了五行,对于我来说,消除重复的最佳方法可能并不明显。

@classmethod
def __get_start_words(cls, iterable, n, start_words):
    iterator, buffer, sentinel = iter(iterable), Deque(maxlen=n), object()
    for _ in range(n):
        item = next(iterator, sentinel)
        if item is sentinel:
            # raise ValueError('iterable was too short to satisfy n')
            break
        buffer.append(item)
        yield item
    start_words[buffer.prefix] += 1
    while True:
        if buffer[0][-1] in cls.TERMINATORS:
            start_words[buffer.suffix] += 1
        item = next(iterator, sentinel)
        if item is sentinel:
            break
        buffer.append(item)
        yield item
Run Code Online (Sandbox Code Playgroud)

是否有一种有效且清晰的方法可以在类或方法中一次编写最后五行?


附录

在回答这个问题就什么prefix以及suffix是,这里是Deque类:

class Deque(collections.deque):
    """Deque([iterable[, maxlen]]) -> Deque instance"""

    @property
    def prefix(self):
        """Property allowing capture of all but last item in deque."""
        item = self.pop()
        value = tuple(self)
        self.append(item)
        return value

    @property
    def suffix(self):
        """Property allowing capture of all but first item in deque."""
        item = self.popleft()
        value = tuple(self)
        self.appendleft(item)
        return value
Run Code Online (Sandbox Code Playgroud)

第二版

考虑到其他人的意见后,编写了以下方法以提高效率:

@classmethod
def __get_start_words(cls, iterable, n, start_words):
    iterator, buffer, count = iter(iterable), Deque(maxlen=n), 0
    for item, count in zip(iterator, range(n)):
        buffer.append(item)
        yield item
    if count + 1 < n:
        raise ValueError('iterable was too short to satisfy n')
    start_words[buffer.prefix] += 1
    try:
        while True:
            if buffer[0][-1] in cls.TERMINATORS:
                start_words[buffer.suffix] += 1
            item = next(iterator)
            buffer.append(item)
            yield item
    except StopIteration:
        pass
Run Code Online (Sandbox Code Playgroud)

第三版

该方法的第三个版本已改编自Daniel的有见识的答案:

@classmethod
def __get_start_words(cls, iterable, n, start_words):
    count, buffer = 0, Deque(maxlen=n)
    for count, item in enumerate(iterable, 1):
        yield item
        buffer.append(item)
        if count == n:
            start_words[buffer.prefix] += 1
        if count >= n and buffer[0][-1] in cls.TERMINATORS:
            start_words[buffer.suffix] += 1
    if count < n:
        raise ValueError('iterable was too short to satisfy n')
Run Code Online (Sandbox Code Playgroud)

最终版本

此方法比我的第一个版本好得多-感谢在这里为我提供帮助的人们。

@classmethod
def __get_start_words(cls, iterable, n, start_words):
    buffer = Deque(maxlen=n)
    for count, item in enumerate(iterable, 1):
        yield item
        buffer.append(item)
        if count == n:
            start_words[buffer.prefix] += 1
        if count >= n and buffer[0][-1] in cls.TERMINATORS:
            start_words[buffer.suffix] += 1
    if len(buffer) < n:
        raise ValueError('iterable was too short to satisfy n')
Run Code Online (Sandbox Code Playgroud)

Dan*_*iel 5

使用for-loop:

@classmethod
def __get_start_words(cls, iterable, n, start_words):
    buffer = Deque(maxlen=n)
    for idx, item in enumerate(iterable, 1):
        buffer.append(item)
        yield item
        if idx == n:
            start_words[buffer.prefix] += 1
        if idx >= n and buffer[0][-1] in cls.TERMINATORS:
            start_words[buffer.suffix] += 1
    if len(buffer) < n:
        raise ValueError('iterable was too short to satisfy n')
Run Code Online (Sandbox Code Playgroud)

关于第二个版本的一些想法:count使用时不需要islice

for item in islice(iterator, n):
    buffer.append(item)
    yield item
if len(buffer) < n:
    raise ValueError('iterable was too short to satisfy n')
Run Code Online (Sandbox Code Playgroud)

进一步的重构导致:

@classmethod
def __get_start_words(cls, iterable, n, start_words):
    iterable = iter(iterable)
    buffer = deque(islice(iterable, n-1))
    yield from buffer
    if len(buffer) < n - 1:
        raise ValueError('iterable was too short to satisfy n')
    start_words[tuple(buffer)] += 1
    for item in iterable:
        buffer.append(item)
        yield item
        first = buffer.popleft()
        if first[-1] in cls.TERMINATORS:
            start_words[tuple(buffer)] += 1
Run Code Online (Sandbox Code Playgroud)