python 中的协程提供了什么来改进简单的消费者/生产者设置?
我读过一些关于协程的内容,特别是关于 python 的协程,但有些东西对我来说并不完全明显。
我已经实现了一个生产者/消费者模型,其基本版本如下:
#!/usr/bin/env python
class MyConsumer(object):
def __init__(self, name):
self.__name = name
def __call__(self, data):
return self.observer(data)
def observer(self, data):
print self.__name + ': ' + str(data)
class MyProducer(object):
def __init__(self):
self.__observers = []
self.__counter = 0
def add_observer(self, observer):
self.__observers.append(observer)
def run(self):
while self.__counter < 10:
for each_observer in self.__observers:
each_observer(self.__counter)
self.__counter += 1
def main():
consumer_one = MyConsumer('consumer one')
consumer_two = MyConsumer('consumer two')
producer = MyProducer()
producer.add_observer(consumer_one)
producer.add_observer(consumer_two)
# run
producer.run()
if __name__ == "__main__":
main()
显然,MyConsumer 也可以有用于生产的例程,因此可以轻松构建数据管道。正如我在实践中实现的那样,定义了一个基类来实现消费者/生产者模型的逻辑,并实现了在子类中覆盖的单个处理函数。这使得使用易于定义的隔离处理元素生成数据管道变得非常简单。
在我看来,这似乎是为协程提供的典型应用程序类型,例如在经常引用的教程中: http://www.dabeaz.com/coroutines/index.html。不幸的是,我并不清楚协程相对于上面的实现有什么优势。我可以看到,在可调用对象更难处理的语言中,可以得到一些东西,但对于 python 来说,这似乎不是问题。
有人能为我解释一下吗?谢谢。
编辑:抱歉,上面代码中的生产者从 0 计数到 9 并通知消费者,然后消费者打印出他们的名字,后面跟着计数值。
I've read a little about coroutines, in particular with python, and something is not entirely obvious to me.
I have implemented a producer/consumer model, a basic version of which is as follows:
#!/usr/bin/env python
class MyConsumer(object):
def __init__(self, name):
self.__name = name
def __call__(self, data):
return self.observer(data)
def observer(self, data):
print self.__name + ': ' + str(data)
class MyProducer(object):
def __init__(self):
self.__observers = []
self.__counter = 0
def add_observer(self, observer):
self.__observers.append(observer)
def run(self):
while self.__counter < 10:
for each_observer in self.__observers:
each_observer(self.__counter)
self.__counter += 1
def main():
consumer_one = MyConsumer('consumer one')
consumer_two = MyConsumer('consumer two')
producer = MyProducer()
producer.add_observer(consumer_one)
producer.add_observer(consumer_two)
# run
producer.run()
if __name__ == "__main__":
main()
Obviously, MyConsumer could have routines for producing as well and so a data pipeline can be built easily. As I have implemented this in practice, a base class is defined that implements the logic of the consumer/producer model and single processing function is implemented that is overwritten in child classes. This makes it very simple to produce data pipelines with easily defined, isolated processing elements.
This seems to me to be typical of the kinds of applications that are presented for coroutines, for example in the oft quoted tutorial: http://www.dabeaz.com/coroutines/index.html. Unfortunately, it is not apparent to me what the advantages of coroutines are over the implementation above. I can see that in languages in which callable objects are more difficult to handle, there is something to be gained, but in the case of python, this doesn't seem to be an issue.
Can anybody shed some light on this for me? Thanks.
edit: Apologies, the producer in the above code counts from 0 to 9 and notifies the consumers, which then print out their name followed by the count value.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
使用协程方法时,消费者和生产者代码有时都可以更简单。在您的方法中,至少其中一个必须被编写为有限状态机(假设涉及某种状态)。
使用协程方法,它们本质上是独立的进程。
一个例子会有所帮助:
以您提供的示例为例,但现在假设消费者仅每第二个输入打印一次。您的方法需要添加一个对象成员,指示接收到的输入是奇数样本还是偶数样本。
使用协程时,只需循环输入,丢弃第二个输入。 “状态”由代码中的当前位置隐式维护:
When using the coroutines approach, both the consumer and the producer code can be simpler sometimes. In your approach, at least one of them must be written as a finite-state-machine (assuming some state is involved).
With the coroutines approach they are essentially independent processes.
An example would help:
Take the example you provided but now assume the consumer prints only every 2nd input. Your approach requires adding an object member indicating whether the received input is an odd or even sample.
When using a coroutine, one would just loop over the input, dropping every second input. The 'state' is implicitly maintained by the current position in the code: