<div class="gmail_quote">On Thu, Nov 15, 2012 at 9:11 PM, Andrew Barnert <span dir="ltr"><<a href="mailto:abarnert@yahoo.com" target="_blank">abarnert@yahoo.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
The clauses have *always* been out of order. In the function, the "if" comes<br>
between the "for" and the yield expression. In the expression, the "for" comes<br>
in between. If the clause order implies the statement order (I would have put it<br>
in terms of the clause structure implying the scoping, but they're effectively<br>
the same idea), then our syntax has been wrong since list comprehensions were<br>
added in 2.0. So, I think (and hope!) that implication was never intended.<br></blockquote><div><br>One, and only one, clause in a comprehension or generator expression is written out of sequence: the innermost clause is lifted out and written first. The rest of the expression is just written in the normal statement order with the colons and newlines omitted. This is why you can chain comprehensions to arbitrary depths without any ambiguity from the compiler's point of view:<br>
<br>>>> seq = [0, [0, [0, 1]]]<br>>>> [z for x in seq if x for y in x if y for z in y if z]<br>[1]<br><br>(Note: even though you *can* chain the clauses like this, please don't, as it's thoroughly unreadable for humans, even though it makes sense to the compiler)<br>
<br>So *if* a context management clause was added to comprehension syntax, it couldn't reasonably be added using the same design as was used to determine the placement of the current iteration and filtering clauses.<br>
<br>If the determination is "place it at the end, and affect the whole comprehension/generator expression regardless of the number of clauses", then you're now very close to the point of it making more sense to propose allowing context managers on arbitrary expressions, as it would be impossible to explain why this was allowed:<br>
<br> lines = list(line for line in f with open(name) as f)<br><br>But this was illegal:<br><br> lines = (f.readlines() with open(name) as f)<br><br>And if arbitrary subexpressions are allowed, *then* you're immediately going to have people wanting a "bind" builtin:<br>
<br> class bind:<br> def __init__(self, value):<br> self.value = value<br> def __enter__(self):<br> return self.value<br> def __exit__(self, *args):<br> pass<br><br>
if (m is None with bind(pattern.match(data) as m):<br> raise ValueError("{} does not match {}".format(data, pattern))<br> # Do things with m...<br><br>This is not an endorsement of the above concepts, just making it clear that I don't believe that attempting to restrict this to generator expressions is a viable proposal, as the restriction is far too arbitrary (from a user perspective) to form part of a coherent language design.<br>
<br>Generator expressions, like lambda expressions, are deliberately limited. If you want to avoid those limits, it's time to upgrade to a named generator or function. If you feel that puts things in the wrong order in your code then please, send me your use cases so I can considering adding them as examples in PEP 403 and PEP 3150. If you really want to enhance the capabilities of expressions, then the more general proposal is the only one with even a remote chance, and that's contingent on proving that the compiler can be set up to give generator expressions the semantics you propose (Off the top of my head, I
suspect it should be possible, since the compiler will know it's in a
comprehension or generator expression by the time it hits the with
token, but there may be other technical limitations that ultimately rule
it out). <br><br>Cheers,<br>Nick.<br> <br></div><div>-- <br></div></div>Nick Coghlan | <a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a> | Brisbane, Australia<br>