有没有办法干掉这台发电机?

时间:2017-11-02 20:23:58

标签: python python-3.x iterator generator dry

以下方法在我的课程中,并在完成其工作之前尝试自我填充。引物在它作为其后的处理循环的工作时很懒惰。在这两个循环中重复了五行,对我来说,消除重复的最佳方法是什么并不明显。

@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

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

附录

在回答有关prefixsuffix的问题时,这里是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

第二版

考虑到其他人不得不说的话,为了提高效率,我写了以下方法:

@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

第三版

该方法的第三个版本改编自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')

最终版

这种方法明显优于我的第一个版本 - 感谢那些帮助我的人。

@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')

1 个答案:

答案 0 :(得分:4)

使用for - 循环:

@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')

关于您的第二个版本的一些想法:使用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')

进一步重构导致:

@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