Python -- 在多个消费者中使用一个生成器

2 投票
1 回答
1525 浏览
提问于 2025-04-17 20:37

我有一个生成器,供不同的消费者使用。每个消费者可以从这个生成器中获取不同的项目,所以我不能只用一个大循环来处理所有的项目。我想要的是完全消费掉这个生成器。该怎么做呢?

# -*- coding: utf-8 -*-
MEALS = ['Oysters', 'Consommé', 'Lamb', 'Rice', 'Sirloin','Banana', 'Pastry']

def server():
    for n in MEALS:
        yield n

def client(course, take):
    meal = []
    for _ in range(take):
        some_meal = next(course)
        meal.append(some_meal)
    return meal

if __name__ == '__main__':
    #print("Available meals: ", list(MEALS))
    course = server()
    try:
        while True:
            meal = client(course, 3)
            print("First client: ", meal)
            meal = client(course, 2)
            print("Second client: ", meal)
    except StopIteration:
        pass

当前输出:

First client:  ['Oysters', 'Consommé', 'Lamb']
Second client:  ['Rice', 'Sirloin']

但是甜点在哪里呢??

期望输出:

First client:  ['Oysters', 'Consommé', 'Lamb']
Second client:  ['Rice', 'Sirloin']
First client:  ['Banana', 'Pastry']

更新 下面接受的解决方案加上对返回列表的测试是可以的,只是我简化了示例代码(在client中可能会有很多next语句)。我现在需要的是一种方法,让client函数在第一次抛出StopIteration时立即返回。所以我添加了一个后续问题,关于在遇到第一次StopIteration时退出函数的最佳方法

1 个回答

4

在第二次执行while循环时,server这个生成器只剩下2个可以提供的项目,而当client()函数尝试获取3个元素时,就会引发StopIteration异常。

你需要在client()函数中处理StopIteration异常:

def client(course, take):
    meal = []
    for _ in range(take):
        try:
            some_meal = next(course)
            meal.append(some_meal)
        except StopIteration:
            pass
    return meal

现在,既然客户端会处理StopIteration,你就得以不同的方式处理while循环;如果client()没有返回任何元素,那说明你的server一定是空的:

while True:
    meal = client(course, 3)
    if not meal:
        break
    print("First client: ", meal)
    meal = client(course, 2)
    print("Second client: ", meal)
    if not meal:
        break

你在这里缺少了一些来自Python标准库的小技巧。你可以用iter()重新实现你的server

def server():
    return iter(MEALS)

而且你可以使用itertools.islice()来处理你的客户端:

from itertools import islice

def client(course, take):
    return list(islice(course, take))

撰写回答