如何在 Python 中使用带有协程的装饰器?

How to use a decorator with a coroutine in Python?

我正在尝试编写一个程序来生成两个相互通信的进程。我已经阅读了有关协程的信息,并认为这次采用它会很好,并且由于协程需要在使用前启动,所以我认为让装饰器自动执行此操作会很好。

import multiprocessing as mp
import random
import time
import os
from datetime import datetime, timedelta
from functools import wraps

output, input = mp.Pipe()



def co_deco(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        cr = func(*args, **kwargs)
        cr.send(None)
        return cr
    return wrapper

class sender(mp.Process):
    def __init__(self, pipe):
        mp.Process.__init__(self)
        self.pipe = pipe

    def run(self):
        print('RECEIVER PID: ', os.getpid() )
        while True:
            self.pipe.send( random.randint(0,10) )
            time.sleep(1)

class receiver(mp.Process):
    def __init__(self, pipe):
        mp.Process.__init__(self)
        self.pipe = pipe

    def run(self):
        while True:
            self.coroutine.send( self.pipe.recv() )

    @co_deco
    def coroutine(self):
        while True:
            msg = yield
            print( datetime.now(), msg )



if __name__ == '__main__':
    mp.freeze_support()

    sen = sender(pipe=input)
    rec = receiver(pipe = output)

    sen.start()
    rec.start()

sen 进程每秒向 rec 进程发送一个随机整数。每当一个整数到达时,coroutine 方法(rec)将它绑定到 msg 并用当前时间打印出来。

我发现代码没有问题,但显示错误消息:

self.coroutine.send( self.pipe.recv() )
AttributeError: 'function' object has no attribute 'send'

我假设装饰协程有问题,但我不知道问题到底是什么以及如何解决它。我想得到一些帮助。

您忘记调用协程了:

def run(self):
    # Create and initialize the coroutine
    cr = self.coroutine()

    while True:
        # Send the data
        cr.send( self.pipe.recv() )

如果你希望它被class绑定,就是这样

def co_deco(func):
    cr = func()
    cr.send(None)
    return cr


@co_deco
def coroutine():
    while True:
        msg = yield
        print( datetime.now(), msg )

对于实例绑定,就是这样。

def co_deco(func):
    @property
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        try:
            # Get the coroutine from the instance object under a 
            # name with a leading underscore
            return getattr(self, "_" + func.__name__)
        except AttributeError:
            pass

        cr = func(self, *args, **kwargs)
        # Set the coroutine from the instance object under a 
        # name with a leading underscore
        setattr(self, "_" + func.__name__, cr)
        cr.send(None)
        return cr
    return wrapper


@co_deco
def coroutine(self):
    while True:
        msg = yield
        print( datetime.now(), msg )