[Twisted-Python] Design Pattern for Iterating Through Lists
Jp Calderone
exarkun at divmod.com
Mon Mar 14 12:04:04 EST 2005
On Mon, 14 Mar 2005 10:37:15 -0600, Ken Kinder <ken at kenkinder.com> wrote:
>I've come almost to the point of making a template for this kind of
> operation:
>
> d = Deferred()
> d.callback(0)
>
> results = []
>
> def doItem(void, eggs):
> return whatever(eggs)
>
> def processResult(result):
> results.append(result)
>
> for spam in list:
> d.addCallback(doItem, spam['eggs'])
> d.addCallback(processResult)
>
> d.addCallback(lambda _: results)
> return d
>
> The reason I'm calling back on doItem is that lambda will evaluate its
> variables one for the iteration, causing only the first evaluation of
> spam['eggs'] to be passed for each item in the list. Is there a more
> readable/efficient way of doing this? Note that I'm not using a
> DeferredList because I want everything processed serially, not in parallel.
Actually, I think you meant "last evaluation" rather than first.
One solution is just to wrap the above into a function:
def serially(processor, items):
results = []
d = defer.Deferred()
for elem in items:
d.addCallback(lambda ign, elem=elem: processor(elem))
d.addCallback(results.append)
d.addCallback(lambda ign: results)
d.callback(None)
return d
Now you can call serially whenever you need this operation and forget how unpleasant the implementation is :)
Another approach involves building up the Deferred chain gradually rather than all at once:
def serially(processor, items):
results = []
d = defer.Deferred()
toBeProcessed = iter(items)
def doItem(ignored):
for elem in toBeProcessed:
defer.maybeDeferred(
processor, elem).addCallback(
results.append).addCallback(
doItem)
break
else:
d.callback(results)
return d
It's a bit more code, and I'm still not sure how I feel about that abuse of for loops (but is handling StopIteration manually any better? dunno), but it avoids building up a large stack inside the Deferred, which can be a good thing - a large number of synchronous results will probably cause Python to raise a RuntimeError in your version or my first version, since Deferred processes its callbacks recursively.
Hope this helps,
Jp
More information about the Twisted-Python
mailing list