将多个无限生成器对象组合到一个生成器中

时间:2019-07-19 23:05:06

标签: python python-asyncio

目的是读取多个游戏手柄的输入并在Python中处理它。我正在使用evdev library

根据文档,假设使用给定的设备对象dev,可以使用功能dev.read_loop()轻松读取Gamepad发送的事件。此函数调用返回调试器告知的generator object。然后,可以使用一个简单的for循环来迭代生成器。它将一直阻塞,直到发生新事件,然后使用在for循环中使用的任何代码处理该事件。

现在,我想使用一个自定义类将事件“流”捆绑到多个游戏手柄上(这更像为它们分配ID,跟踪断开连接等),这就是我的问题所在。 我找不到一种方法来创建这样的生成器,该生成器将所有游戏手柄的事件组合在一起。游戏手柄对象在列表中给出。

提供的示例代码尝试模仿所需的行为,但失败。构造函数将打印所有可用的设备,然后对其进行初始化。方法global_generator()是我解决此问题的方法。问题是,外部for循环for device in self.__gamepads永远不会进行,因为第一个生成器是无限的,因此将始终且仅转发来自第一个控制器的事件。我尝试使用itertools.chain(),但是它们有同样的问题,因为第一个生成器永远不会结束。所以我想要的基本上是:“一旦这n个生成器之一产生结果,就进一步产生它(并因此将它们组合)”。由于无法同时循环我的设备(即它们的生成器)的问题,我拥有的所有想法(例如创建队列或其他任何东西)总是会停止。

我在文档中看到了该部分,其中显示了如何从多个设备读取信息。问题是他们总是打印结果,但没有显示如何以我想要的方式传递结果。

import hardware.ControllerHandler as ch

controller_handler = ch.ControllerHandler()
global_generator = controller_handler.global_generator()

for event in global_generator:
    #event = ce.ControllerEvent(event)
    print(event)

import evdev
from evdev import InputDevice
import asyncio


class ControllerHandler:

    def __init__(self):
        print(evdev.list_devices())
        self.__gamepads = list(map(InputDevice, evdev.list_devices()))

    @property
    def gamepads(self):
        return self.__gamepads

    @property
    def count(self):
        return len(self.__gamepads)

    async def global_generator(self):
        for device in self.__gamepads:
            async for event in device.async_read_loop():
                yield event

因此,预期结果是生成器返回任何基础事件,而不仅仅是第一个游戏手柄上的事件。

1 个答案:

答案 0 :(得分:3)

您可以使用aiostream库合并多个异步生成器:

    async def global_generator(self):
        loops = [device.async_read_loop() for device in self.__gamepads]
        async with aiostream.stream.merge(*loops).stream() as merged:
            async for event in merged:
                yield event

如果您不希望依赖aiostream,请参阅this answer,以获取有关如何使用纯异步来合并流的说明。

请注意,由于生成的生成器是异步的,因此您需要在协程内部使用async for对其进行迭代。