Рекурсивные генераторы и send () - PullRequest
2 голосов
/ 22 июля 2011

Кто-нибудь знает, как 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) не ожидает отправки значения. Или что-то. Не совсем понятно, как значение recurse внутреннего цикла получает None; вызывающий абонент звонит send().

В конечном счете, цель в основном состоит в том, чтобы рекурсивно пройтись по древовидной структуре, но дать возможность самому высокому вызывающему абоненту указать, когда не , чтобы вернуться в субструктуру. например.,

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

1 Ответ

5 голосов
/ 22 июля 2011

Важно понимать, что send () также потребляет!
From http://docs.python.org/reference/expressions.html#generator.send:

Возобновляет выполнение и «отправляет» значение в функцию генератора.Аргумент 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...