Python扭曲:迭代器和yields/inlineCallbacks
各位, 我完全困惑了,所以我什至可能没有正确地询问问题,但这里是:
我有一个使用 inlineCallbacks 的扭曲应用程序。现在我需要定义一个迭代器,这意味着生成器将返回给调用者。但是,迭代器不能被 inlineCallbacks 修饰,可以吗?如果没有,那么我该如何编写这样的代码。
只是澄清一下:目标是 process_loop 需要每隔(比如 5 秒)调用一次,它只能处理其中的一大块(比如 10 秒),然后必须放手。然而,要知道 10 个块(存储在缓存中,这是一个字典的字典),它需要调用一个返回 deferred 的函数。
@inlineCallbacks ### can\'t have inlineCallbacks here, right?
def cacheiter(cached):
for cachename,cachevalue in cached.items():
result = yield (call func here which returns deferred)
if result is True:
for k,v in cachedvalue.items():
yield cachename, k, v
@inlineCallbacks
def process_chunk(myiter, num):
try:
for i in xrange(num):
nextval = myiter.next()
yield some_processing(nextval)
returnValue(False)
except StopIteration:
returnValue(True)
@inlineCallbacks
def process_loop(cached):
myiter = cacheiter(cached)
result = yield process_chunk(myiter, 10)
if not result:
print 'More left'
reactor.callLater(5, process_loop, cached)
else:
print 'All done'
Folks,
Am thoroughly confused, so it's possible I am not even asking things correctly, but here goes:
I have a twisted application using inlineCallbacks. Now I need to define an iterator which will mean a generator is returned to the caller. However, the iterator cannot be inlineCallbacks decorated, can it be? If not, then how I do I code something like this.
Just to clarify: the goal is process_loop needs to be called every, say 5, seconds, it can process only ONE chunk of, say 10, and then it has to let go. However, to know that chunk of 10 (stored in cached, which is a dict of a dict), it needs to call a function that returns deferred.
@inlineCallbacks ### can\'t have inlineCallbacks here, right?
def cacheiter(cached):
for cachename,cachevalue in cached.items():
result = yield (call func here which returns deferred)
if result is True:
for k,v in cachedvalue.items():
yield cachename, k, v
@inlineCallbacks
def process_chunk(myiter, num):
try:
for i in xrange(num):
nextval = myiter.next()
yield some_processing(nextval)
returnValue(False)
except StopIteration:
returnValue(True)
@inlineCallbacks
def process_loop(cached):
myiter = cacheiter(cached)
result = yield process_chunk(myiter, 10)
if not result:
print 'More left'
reactor.callLater(5, process_loop, cached)
else:
print 'All done'
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你说得对,你无法在
cacheiter
中表达你想要表达的内容。inlineCallbacks
装饰器不会让您拥有返回迭代器的函数。如果用它修饰一个函数,那么结果是一个始终返回Deferred
的函数。这就是它的用途。造成这一问题的部分原因是迭代器不能很好地处理异步代码。如果在生成迭代器的元素时涉及到 Deferred,那么从迭代器中出来的元素将首先成为 Deferred。
您可能会做这样的事情来解释这一点:
这可以工作,但看起来特别奇怪。由于生成器只能屈服于其调用者(例如,不能屈服于其调用者的调用者),因此
some_jobs
迭代器对此无能为力;只有process_work
中的词法代码可以产生一个 Deferred 到inlineCallbacks
提供的蹦床来等待。如果您不介意这种模式,那么我们可以将您的代码想象成如下所示:
不过,您可以采取的另一种方法是使用
twisted.internet.task.cooperate
。cooperate
接受一个迭代器并使用它,假设使用它的成本可能很高,并将作业分解为多个反应器迭代。从上面获取cacheiter
的定义:You're right that you can't express what you want to express in
cacheiter
. TheinlineCallbacks
decorator won't let you have a function that returns an iterator. If you decorate a function with it, then the result is a function that always returns aDeferred
. That's what it is for.Part of what makes this difficult is that iterators don't work well with asynchronous code. If there's a Deferred involved in producing the elements of your iterator, then the elements that come out of your iterator are going to be Deferreds first.
You might do something like this to account for that:
This can work, but it looks particularly weird. Since generators can only yield to their caller (not, for example, to their caller's caller), the
some_jobs
iterator can't do anything about this; only code lexically withinprocess_work
can yield a Deferred to theinlineCallbacks
-provided trampoline to wait on.If you don't mind this pattern, then we could imaging your code being written something like:
Another approach you might be able to take, though, is to use
twisted.internet.task.cooperate
.cooperate
takes an iterator and consumes it, assuming that consuming it is potentially costly, and splitting up the job over multiple reactor iterations. Taking the definition ofcacheiter
from above:我认为你正在尝试这样做:
当 genexp 无法表达你想要返回的内容时,你可以编写一个闭包:
I think you're trying to do this:
When a genexp can't express what you're trying to return you can write a closure:
尝试将迭代器编写为
DeferredGenerator
。Try writing your iterator as a
DeferredGenerator
.