Python 将asyncio与外部库中的非异步回调方法一起使用

Python 将asyncio与外部库中的非异步回调方法一起使用,python,asynchronous,python-asyncio,gpiozero,Python,Asynchronous,Python Asyncio,Gpiozero,我正在使用python库处理Raspberry Pi上的简单GPIO设备(我在这里使用MotionSensor作为示例): 导入异步IO 从gpiozero导入运动传感器 类MotionSensorHandler(): __whenMotionCallback=None def u uu init uuuuu(self、pin、whenMotionCallback): #whenMotionCallback是一个异步函数 self.\uuuWhenMotionCallback=whenMotio

我正在使用python库处理Raspberry Pi上的简单GPIO设备(我在这里使用MotionSensor作为示例):

导入异步IO
从gpiozero导入运动传感器
类MotionSensorHandler():
__whenMotionCallback=None
def u uu init uuuuu(self、pin、whenMotionCallback):
#whenMotionCallback是一个异步函数
self.\uuuWhenMotionCallback=whenMotionCallback
#只需使用gpiozero lib初始化传感器
运动传感器=运动传感器(针脚)
#检测到运动时调用的方法
motionSensor.when\U MOTIONE=self.whenMotion
异步def whenMotion(自):
等待self.\uuuu whenMotionCallback()
我这里的问题是,我试图给一个
async
函数提供对
motionSensor.when\u motion
的回调

所以我得到了一个错误,
whenMotion
函数是
async
但从不
wait
但实际上我不能等待它:

#将无法工作,因为MotionSensor()未使用asyncio
motionSensor.when\u motion=等待self.whenMotion

您知道如何将我的
async
函数分配给一个none-one吗?

如果您使用协程执行此操作,则需要获取并运行事件循环。我假设您使用的是python 3.7,在这种情况下,您可以执行以下操作:

import asyncio
from gpiozero import MotionSensor


class MotionSensorHandler():
    __whenMotionCallback = None

    def __init__(self, pin, whenMotionCallback):
        # whenMotionCallback is an async function
        self.__whenMotionCallback = whenMotionCallback

        # Just init the sensor with gpiozero lib
        motionSensor = MotionSensor(pin)

        # Method to call when motion is detected
        loop = asyncio.get_event_loop()
        motionSensor.when_motion = loop.run_until_complete(self.whenMotion())
        loop.close()

    async def whenMotion(self):
        await self.__whenMotionCallback()

如果您使用的是python 3.8,那么只需使用
asyncio.run
,而不是所有显式地获取和运行事件循环。

如果您使用协同程序来实现这一点,则需要获取和运行事件循环。我假设您使用的是python 3.7,在这种情况下,您可以执行以下操作:

import asyncio
from gpiozero import MotionSensor


class MotionSensorHandler():
    __whenMotionCallback = None

    def __init__(self, pin, whenMotionCallback):
        # whenMotionCallback is an async function
        self.__whenMotionCallback = whenMotionCallback

        # Just init the sensor with gpiozero lib
        motionSensor = MotionSensor(pin)

        # Method to call when motion is detected
        loop = asyncio.get_event_loop()
        motionSensor.when_motion = loop.run_until_complete(self.whenMotion())
        loop.close()

    async def whenMotion(self):
        await self.__whenMotionCallback()

如果您使用的是python 3.8,则只需使用
asyncio.run
,而不是所有显式获取和运行事件循环。

如果这是在循环中运行的,并且
在运动不需要返回值时,可以执行以下操作:

。。。
motionSensor.when\U MOTIONE=self.whenMotion
def启动时(自身):
asyncio.Survey_future(self.u whenMotionCallback())

这将在事件循环中安排异步回调,并使库的调用代码保持同步。

如果这是在循环中运行的,并且
当\u motion
不需要返回值时,可以执行以下操作:

。。。
motionSensor.when\U MOTIONE=self.whenMotion
def启动时(自身):
asyncio.Survey_future(self.u whenMotionCallback())

这将在事件循环中安排异步回调,并保持库的调用代码同步。

因此,经过研究,我发现我必须创建一个新的异步IO循环,以非异步方法执行异步脚本。因此,现在我的
whenMotion()
方法不再是
async
,而是使用
sure\u future()
执行一个

导入异步IO
从gpiozero导入运动传感器
类MotionSensorHandler():
__whenMotionCallback=None
def u uu init uuuuu(self、pin、whenMotionCallback):
#whenMotionCallback是一个异步函数
self.\uuuWhenMotionCallback=whenMotionCallback
#只需使用gpiozero lib初始化传感器
运动传感器=运动传感器(针脚)
#检测到运动时调用的方法
motionSensor.when\U MOTIONE=self.whenMotion
def启动时(自身):
#创建新的异步IO循环
loop=asyncio.new\u event\u loop()
asyncio.set\u event\u循环(循环)
future=asyncio。确保_future(self._executewhenomationcallback())#执行异步方法
循环。运行_直到_完成(未来)
loop.close()
异步定义\uuuu执行WhenMotionCallback(自):
等待self.\uuuu whenMotionCallback()

所以经过研究,我发现我必须创建一个新的asyncio循环,以非异步方法执行异步脚本。因此,现在我的
whenMotion()
方法不再是
async
,而是使用
sure\u future()
执行一个

导入异步IO
从gpiozero导入运动传感器
类MotionSensorHandler():
__whenMotionCallback=None
def u uu init uuuuu(self、pin、whenMotionCallback):
#whenMotionCallback是一个异步函数
self.\uuuWhenMotionCallback=whenMotionCallback
#只需使用gpiozero lib初始化传感器
运动传感器=运动传感器(针脚)
#检测到运动时调用的方法
motionSensor.when\U MOTIONE=self.whenMotion
def启动时(自身):
#创建新的异步IO循环
loop=asyncio.new\u event\u loop()
asyncio.set\u event\u循环(循环)
future=asyncio。确保_future(self._executewhenomationcallback())#执行异步方法
循环。运行_直到_完成(未来)
loop.close()
异步定义\uuuu执行WhenMotionCallback(自):
等待self.\uuuu whenMotionCallback()

当设置When_motion属性时,gpiozero将创建一个执行回调的新线程(这一点没有很好的文档记录)。如果回调应该在主异步IO循环中执行,那么需要将控制权传递回主线程

这个方法可以帮你做到这一点。本质上,当等待发生时,它将回调添加到主异步IO循环调用的任务列表中

但是,异步IO循环对于每个线程都是本地的:请参阅

因此,当在主异步线程中创建gpiozero对象时,需要在调用回调时使该循环对象对该对象可用

对于调用asyncio MQTT方法的PIR,我是这样做的:

class PIR:
    def __init__(self, mqtt, pin):
        self.pir = MotionSensor(pin=pin)
        self.pir.when_motion = self.motion
        # store the mqtt client we'll need to call
        self.mqtt = mqtt
        # This PIR object is created in the main thread
        # so store that loop object
        self.loop = asyncio.get_running_loop()

    def motion(self):
        # motion is called in the gpiozero monitoring thread
        # it has to use our stored copy of the loop and then
        # tell that loop to call the callback:
        self.loop.call_soon_threadsafe(self.mqtt.publish,
                                       f'sensor/gpiod/pir/kitchen', True)

当设置When_motion属性时,gpiozero将创建一个执行回调的新线程(这一点没有很好的文档记录)。如果回调应该在主异步IO循环中执行,那么需要将控制权传递回主线程

这个方法可以帮你做到这一点。本质上,当等待发生时,它将回调添加到主异步IO循环调用的任务列表中

然而