递归生成器和 send()

发布于 2024-11-25 17:02:28 字数 1288 浏览 1 评论 0原文

有谁知道 send() 递归使用时如何与生成器一起使用?我预计该值将传递给当前生成器,然后当前生成器可以将其传递给递归生成器......但似乎情况并非如此?一些示例代码:

def Walk(obj):
  recurse = (yield obj)
  if not recurse:
    print 'stop recurse:', recurse
    return

  if isinstance(obj, list):
    print 'is list:', obj
    for item in obj:
      print 'item loop:', item
      walker = Walk(item)
      for x in walker:
        print 'item walk:', x
        recurse = (yield x)
        print 'item walk recurse:', recurse
        walker.send(recurse)

root = ['a', ['b.0', ['b.0.0']]]

walker = Walk(root)
for i, x in enumerate(walker):
  print i, x
  print 'send true'
  walker.send(True)

所需的输出应该是每个级别递归的每个值:

0 ['a', ['b.0', ['b.0.0']]]
1 'a'
2 ['b.0', ['b.0.0']]
3 'b.0'
4 ['b.0.0']
5 'b.0.0'

最终发生的情况是:

0 ['a', ['b.0', ['b.0.0']]]
send true
is list: ['a', ['b.0', ['b.0.0']]]
item loop: a
item walk: a
item walk recurse: None
stop recurse: None

看起来带有 recurse = (yield) 的内循环不等待值待发送。或者什么。目前尚不清楚内循环递归值如何变得无;它的调用者确实调用了send()

最终,目标基本上是递归地遍历树结构,但让最顶层的调用者能够指定何时递归到子结构。例如,

walker = Walk(root)
for node in walker:
  if CriteriaMet(node):
    walker.send(True)
  else:
    walker.send(False)

Does anyone know how send() works with generators when used recursively? I expected the value to be passed to the current generator, which could then pass it down to the recursed-generator...but it seems that is not the case? Some example code:

def Walk(obj):
  recurse = (yield obj)
  if not recurse:
    print 'stop recurse:', recurse
    return

  if isinstance(obj, list):
    print 'is list:', obj
    for item in obj:
      print 'item loop:', item
      walker = Walk(item)
      for x in walker:
        print 'item walk:', x
        recurse = (yield x)
        print 'item walk recurse:', recurse
        walker.send(recurse)

root = ['a', ['b.0', ['b.0.0']]]

walker = Walk(root)
for i, x in enumerate(walker):
  print i, x
  print 'send true'
  walker.send(True)

The desired output is should be each value at each level recursion:

0 ['a', ['b.0', ['b.0.0']]]
1 'a'
2 ['b.0', ['b.0.0']]
3 'b.0'
4 ['b.0.0']
5 'b.0.0'

What ends up happening is:

0 ['a', ['b.0', ['b.0.0']]]
send true
is list: ['a', ['b.0', ['b.0.0']]]
item loop: a
item walk: a
item walk recurse: None
stop recurse: None

It looks like the inner-loop with recurse = (yield) doesn't wait for a value to be sent. Or something. Its not really clear how the inner-loop recurse value is getting None; its caller does call send().

Ultimately, the goal is basically to walk over a tree-structure recursively, but have the top-most caller be able to specify when not to recurse into a substructure. e.g.,

walker = Walk(root)
for node in walker:
  if CriteriaMet(node):
    walker.send(True)
  else:
    walker.send(False)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

万劫不复 2024-12-02 17:02:28

需要意识到的重要一点是 send() 也会消耗!
来自 http://docs.python.org/reference/expressions.html#generator。发送

恢复执行并将一个值“发送”到生成器函数中。
value 参数成为当前yield 表达式的结果。
send() 方法返回生成器生成的下一个值,或者
如果生成器退出而没有产生另一个生成器,则引发 StopIteration
价值。当调用send()来启动生成器时,必须调用它
以 None 作为参数,因为没有yield 表达式
可以收到值。

以下是对代码的快速修改,使其按预期输出:

def Walk(obj):
  recurse = (yield obj)
  if not recurse:
    #print 'stop recurse:', recurse
    return

  if isinstance(obj, list):
    #print 'is list:', obj
    for item in obj:
      #print 'item loop:', item
      walker = Walk(item)

      recurse = None #first send must be None
      while True:
        try:
          x = walker.send(recurse)
        except StopIteration:
          break
        #print 'item walk:', x
        recurse = (yield x)
        #print 'item walk recurse:', recurse

root = ['a', ['b.0', ['b.0.0']]]

walker = Walk(root)
i = 0
x = walker.next()
while True:
  print i, x
  try:
    x = walker.send(True)
  except StopIteration:
    break
  i += 1

输出:

0 ['a', ['b.0', ['b.0.0']]]
1 a
2 ['b.0', ['b.0.0']]
3 b.0
4 ['b.0.0']
5 b.0.0

The important thing to realize is that send() also consumes!
From http://docs.python.org/reference/expressions.html#generator.send:

Resumes the execution and “sends” a value into the generator function.
The value argument becomes the result of the current yield expression.
The send() method returns the next value yielded by the generator, or
raises StopIteration if the generator exits without yielding another
value. When send() is called to start the generator, it must be called
with None as the argument, because there is no yield expression that
could receive the value.

Here is a quick re-working of your code to get it to output as expected:

def Walk(obj):
  recurse = (yield obj)
  if not recurse:
    #print 'stop recurse:', recurse
    return

  if isinstance(obj, list):
    #print 'is list:', obj
    for item in obj:
      #print 'item loop:', item
      walker = Walk(item)

      recurse = None #first send must be None
      while True:
        try:
          x = walker.send(recurse)
        except StopIteration:
          break
        #print 'item walk:', x
        recurse = (yield x)
        #print 'item walk recurse:', recurse

root = ['a', ['b.0', ['b.0.0']]]

walker = Walk(root)
i = 0
x = walker.next()
while True:
  print i, x
  try:
    x = walker.send(True)
  except StopIteration:
    break
  i += 1

Output:

0 ['a', ['b.0', ['b.0.0']]]
1 a
2 ['b.0', ['b.0.0']]
3 b.0
4 ['b.0.0']
5 b.0.0
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文