在Python中将回调转换为生成器?
假设我们有一个库(例如用于XML解析),它接受回调并在每次遇到某个事件时调用回调(例如查找某个XML标记)。我希望能够将这些回调转换为可以通过for循环迭代的生成器。在Python中,不使用或收集所有回调结果(即,使用惰性计算)是否可能实现这一点 例如:在Python中将回调转换为生成器?,python,callback,generator,coroutine,Python,Callback,Generator,Coroutine,假设我们有一个库(例如用于XML解析),它接受回调并在每次遇到某个事件时调用回调(例如查找某个XML标记)。我希望能够将这些回调转换为可以通过for循环迭代的生成器。在Python中,不使用或收集所有回调结果(即,使用惰性计算)是否可能实现这一点 例如: # this is how I can produce the items def callback(item) # do something with each item parser.parse(xml_file, callback
# this is how I can produce the items
def callback(item)
# do something with each item
parser.parse(xml_file, callback=callback)
# this is how the items should be consumed
for item in iter_parse(xml_file):
print(item)
我试图研究是否可以使用协同路由,但似乎协同路由对于从生产者推送数据很有用,而生成器将数据推送到消费者
自然的想法是,生产者和消费者将是协同程序,将执行流来回ping
我成功地获得了一个生产者-消费者模式,该模式使用异步IO循环(与此类似)。但是,它不能像for循环中的生成器那样使用:
import asyncio
q = asyncio.Queue(maxsize=1)
@asyncio.coroutine
def produce(data):
for v in data:
print("Producing:", v)
yield from q.put(v)
print("Producer waiting")
yield from q.put(None)
print("Producer done")
@asyncio.coroutine
def consume():
while True:
print("Consumer waiting")
value = yield from q.get()
print("Consumed:", value)
if value is not None:
# process the value
yield from asyncio.sleep(0.5)
else:
break
print("Consumer done")
tasks = [
asyncio.Task(consume()),
asyncio.Task(produce(data=range(5)))
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
问题是结果不能在for循环中迭代,因为它是由循环管理的
当我重写代码以便从普通函数调用回调时,问题是从回调调用的asyncio.Queue.put()不会阻塞,计算也不会延迟
import asyncio
q = asyncio.Queue(maxsize=1)
def parse(data, callback):
for value in data:
# yield from q.put(value)
callback(value)
@asyncio.coroutine
def produce(data):
@asyncio.coroutine
def enqueue(value):
print('enqueue()', value)
yield from q.put(value)
def callback(value):
print('callback()', value)
asyncio.async(enqueue(value))
parse(data, callback)
print('produce()')
print('produce(): enqueuing sentinel value')
asyncio.async(enqueue(None))
print('produce(): done')
@asyncio.coroutine
def consume():
print('consume()')
while True:
print('consume(): waiting')
value = yield from q.get()
print('consumed:', value)
if value is not None:
# here we'd like to yield and use this in a for loop elsewhere
print(value)
else:
break
print('consume(): done')
tasks = [
asyncio.Task(consume()),
asyncio.Task(produce(range(5)))
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# I'd like:
# for value in iter_parse(data=range(5)):
# print('consumed:', value)
使用asyncio是否可以进行这种计算,或者是否需要使用greenlet或gevent?我似乎处于for循环中,但如果可能的话,我不喜欢依赖另一个库,而且它还没有完全为Python 3做好准备。在这里避免线程有什么特别的原因吗?除非您的xml解析库与这些框架集成,否则使用
asyncio
甚至gevent
是没有意义的。否则,parser.parse
将一直阻塞,直到其完成,这将阻塞asyncio
/gevent
事件循环。由于XML解析不是I/O绑定的,我怀疑您的XML库是否与这两个框架集成,所以您唯一的选择是使用线程。如果在异步服务器中完成此解析,那么您可以比承担计算繁重的负载(无论I/O绑定如何)更早屈服。例如,在boost asio中,在某些情况下使用无堆栈协程来解析简单字符串。请检查Python3.6是否有用。