Сопрограммы (их упомянул бас-гитарист) сложно для непосвященных, так что вот один.Я добавил тестовый код, чтобы вы могли увидеть, как это действительно работает.
class Node(object):
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
# the producing coroutine, it sends data to the consumer
def depth_first_search(self, consumer, depth=0):
""" `consumer` is a started coroutine that yields True to continue a branch """
if consumer.send((self, depth)): # continue this branch?
for child in self.get_child_nodes():
child.depth_first_search(consumer, depth+1)
def get_child_nodes(self):
for node in (self.left, self.right):
if node is not None:
yield node
def __repr__(self):
return "Node(val=%d)" % self.val
def coroutine(func):
""" decorator that declares `func` as a coroutine and starts it """
def starter(*args, **kwargs):
co = func(*args, **kwargs)
next(co) # corotines need to be started/advanced to the first yield
return co
return starter
# the consumer takes data and yields if it wants to continue
@coroutine
def consumer( continue_branch=lambda n,d:True ):
node, depth = (yield True) # first node
while True:
print node, depth # do stuff
node, depth = (yield continue_branch(node, depth))
# testing
tree = Node(5, Node(2, Node(3), Node(4)), Node(6, Node(7), Node(8))) #
cons = consumer()
tree.depth_first_search(cons)# yields all
print
stopper = consumer(lambda n,d: n.val > 2) # skips the children of Node 2
tree.depth_first_search(stopper)
Хитрость в том, что если вы сохраняете роли своих функций, где depth_first_search
дает узлы, вы в конечном итоге получаете ужасный беспорядок... вместо этого узлы создаются и отправляются потребителю.
Поддержка Python для сопрограмм немного неуклюжа (@coroutine
на помощь).Существует хорошее хорошее руководство по Python и множество ресурсов для языков, которые зависят от сопрограмм, таких как Lua.В любом случае, это очень крутая концепция, которую стоит изучить: -)