消耗和生成的Python协程

时间:2011-12-16 16:48:35

标签: python coroutine

在我的网络爬虫中,我有一个类跟踪要抓取的网址,删除重复项等:

class VisitOnlyOnceClerk(object):
    def __init__(self):
        self.visited = set()
        self.to_visit = set()

    def enqueue(self, url, referer):
        if not url in self.visited:
            self.to_visit.add((url, referer))

    def __bool__(self):
        return bool(self.to_visit)

    def __iter__(self):
        while self.to_visit:
            (url, referer) = self.to_visit.pop()
            self.visited.add(url)
            yield (url, referer)

这提供了一个可以添加值的迭代:

clerk = VisitOnlyOnceClerk()
clerk.enqueue(starting_point, starting_point)
for (url, referer) in clerk:
    # get the url, and clerk.enqueue() all the links from it ...

这似乎是一个可以由协程执行的任务。我已经看到了仅仅产生值的协同程序的例子,以及仅消耗价值的例子,但是没有两个同时消耗价值的例子。类似的东西:

def visit_once_clerk():
    visited = set()
    to_visit = set([(yield)])

    for i in to_visit:
        visited.add(i[1])
        extras = (yield i)
        if extras:
            to_visit.union(i for i in extras if i[1] not in visited)

当然,这并不像我认为的那样有效。协同程序甚至是正确的工具吗?在这种情况下使用它们的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

我不认为协程是你的类生成器的方法恕我直言的方式是比Python协程允许你做的方式更易读,易懂和易于使用。

在此我要说明的是我将如何实现协程:

def visit_once_clerk(start_point=None):
    visited = set()
    to_visit = set(start_point or [])

    while to_visit:
        value = (yield to_visit.pop())
        if value and value not in visited:
            to_visit.add(value)

这是应该如何使用的:

clerk = visit_once_clerk(['start'])
print clerk.next()   # Print: start
print clerk.send('test')  # Print: test
print clerk.next()   # Raise StopIteration

注意:您可以看到两个实施之间存在差异,特别是sendenqueue操作,send操作会立即生成值 ITOH < / strong> enqueue会安排新值为任意产生(因为使用了set