Skip to main content
 首页 » 编程设计

python之有什么办法可以让这台发电机干涸吗

2025年05月04日20lonelyxmas

以下方法在我的类(class)中,它会在完成工作之前尝试 self 准备。作为其后的处理循环,引物懒惰地执行其工作。在这两个循环中重复了五行,我不清楚消除重复的最佳方法是什么。

@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 

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


附录

在回答有关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 

第二版

考虑到别人说的,为了效率写了如下方法:

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

请您参考如下方法:

使用 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