[Python-ideas] With clauses for generator expressions

Phil Connell pconnell at gmail.com
Thu Nov 15 12:22:51 CET 2012


On Thu, Nov 15, 2012 at 03:11:07AM -0800, Andrew Barnert wrote:
> > From: Phil Connell <pconnell at gmail.com>
> > Sent: Thu, November 15, 2012 1:24:52 AM
> > 
> > On Wed, Nov 14, 2012 at 07:44:44PM -0800, Andrew Barnert wrote:
> > > 
> > >     upperlines =  (lines.upper() for line in file with open('foo', 'r') as 
> >file)
> > 
> > While this  looks very clean, how do you propose the following should be 
> >written
> > as a  generator expression?
> > 
> > def foo():
> >     with open('foo') as  f:
> >         for line in f:
> >              if 'bar' in line:
> >                  yield line
> 
> Exactly as you suggest (quoting you out of order to make the answer clearer):
> 
> > (line
> > for line in f
> > if bar in 'line'
> > with open('foo') as f)
> 
> 
> > An obvious suggestion is as follows, but I'm  not totally convinced about the
> > out-of-order with, for and if clauses  (compared with the equivalent 
> generator)
> 
> 
> The clauses have *always* been out of order. In the function, the "if" comes 
> between the "for" and the yield expression. In the expression, the "for" comes 
> in between. If the clause order implies the statement order (I would have put it 
> in terms of the clause structure implying the scoping, but they're effectively 
> the same idea), then our syntax has been wrong since list comprehensions were 
> added in 2.0. So, I think (and hope!) that implication was never intended.

I was mostly playing devil's advocate :)

In my experience, the ordering of comprehension clauses is already a source of
confusion for those new to the language. So, if it's not obvious where the "if"
should come it may well make matters worse in this regard (but I wouldn't say
that this is enough to kill the proposal).


> 
> Which means the only question is, which one looks more readable:
> 
> 1. (foo(line) for line in baz(f) if 'bar' in line with open('foo') as f)
> 2. (foo(line) for line in baz(f) with open('foo') as f if 'bar' in line)
> 3. (foo(line) with open('foo') as f for line in baz(f) if 'bar' in line)

To me, 1 feels like it captures the semantics the best - the "with" clause is
tacked onto the generator expression "(foo(line) ... for ... if)" and applies
to the whole of that expression.


Cheers,
Phil

> 
> Or, in the trivial case (where versions 1 and 2 are indistinguishable):
> 
> 1. (line for line in f with open('foo') as f)
> 2. (line for line in f with open('foo') as f)
> 3. (line with open('foo') as f for line in f)
> 
> My own intuition is that 1 is the clearest, and 3 by far the worst. So, that's 
> why I proposed order 1. But I'm not at all married to it.



More information about the Python-ideas mailing list