Jeremy Bowers wrote:
At first I liked this, but the reason that is a syntax error is that it is
"supposed" to be

def f():
    yield (x for x in gen1(arg))

which today on 2.4 returns a generator instance which will in turn
yield one generator instance from the genexp

And it would continue to do so in the future. On the other hand, removing the parens makes it easy to write things like tree traversal algorithms:


def left_to_right_traverse(node):
  yield x for x in node.left
  yield node .value
  yield x for x in node.right

In reality, I expect yielding each item of a sub-iterable to be more common than building a generator that yields generators.

, and I am quite uncomfortable
with the difference between the proposed behaviors with and without the
parens.

Why? Adding parentheses can be expected to have significant effects when it causes things to be parsed differently. Like the example I posted originally:


  [x for x in iterable]  # List comp (no parens == eval in place)
  [(x for x in iterable)] # Parens - generator goes in list

Or, for some other cases where parentheses severely affect parsing:

  print x, y
  print (x, y)

  assert x, y
  assert (x, y)

If we want to pass an iterator into a function, we use a generator expression, not extended call syntax. It makes sense to base a sub-iterable yield syntax on the former, rather than the latter.

Moreover, since "yield" is supposed to be analogous to "return", what does

    return x for x in gen1(arg)

do? Both "it returns a list" and "it returns a generator" have some
arguments in their favor.

No, it would translate to:

  for x in gen1(arg):
    return x

Which is nonsense, so you would never make it legal.

And I just now note that any * syntax, indeed, any syntax at all will
break this.

As you noted, this argument is specious because it applies to *any* change to the yield syntax - yield and return are fundamentally different, since yield allows resumption of processing on the next call to next().


> You know, given the marginal gains this gives anyway,

I'm not so sure the gains will be marginal. Given the penalties CPython imposes on recursive calls, eliminating the nested "next()" invocations could significantly benefit any code that uses nested iterators.

An interesting example where this could apply is:

def flatten(iterable):
  for item in iterable:
    if item is iterable:
      # Do the right thing for self-iterative things
      # like length 1 strings
      yield iterable
      raise StopIteration
    try:
      itr = iter(item):
    except TypeError:
      yield item
    else:
      yield x for x in flatten(item)

Cheers,
Nick.

P.S. Which looks more like executable pseudocode?

def traverse(node):
  yield *node.left
  yield node .value
  yield *node.right

def traverse(node):
  yield x for x in node.left
  yield node .value
  yield x for x in node.right

--
Nick Coghlan   |   [EMAIL PROTECTED]   |   Brisbane, Australia
---------------------------------------------------------------
            http://boredomandlaziness.skystorm.net
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to