[Python-ideas] With clauses for generator expressions

Terry Reedy tjreedy at udel.edu
Sat Nov 17 01:00:19 CET 2012

On 11/16/2012 4:09 AM, Andrew Barnert wrote:
> So far, nearly everyone is discussing things which are tangential, or arguing
> that one of the optional variants is bad. So let me strip down the proposal,
> without any options in it, and expand on a use case. The syntax is:
>      (foo(line) with open('bar') as f for line in baz(f))

OK, that's helpful. Now let me strip down my objection to this: your 
proposal is conceptually wrong because it mixes two distinct and 
different ideas -- collection definition and context management. It 
conflicts with a well-defined notion of long standing.

To explain: in math, one can define a set explicitly by displaying the 
members or implicitly as a subset of based on one or more base sets. 
Using one version of the notation
{0, 2, 4} == {2*i| i in N; i < 3}
The latter is 'set-builder notation' or a 'set comprehension' (and would 
usually use the epsilon-like member symbol instead of 'in'). The idea 
goes back at least a century.

In Python, the latter directly translates to
   {2*i for i in itertools.count() if i < 3} ==
   {i for i in range(0, 5, 2)}
(Python does not require the base collection to match the result class.)
Another pair of examples:
   {(i,j)| i in N, j in N; i+j <= 5}
   {(i,j) for i in count() for j in count if i+j <= 5}

Similar usage in programming go back over half a century.
While notation in both math and CS varies, the components are always 
input source collection variables, conditions or predicates, and an 
output expression.

The Python reference manual documents comprehensions as an alternate 
atomic display form. In Chapter 6, Expressions, Section 2, Atoms,

"For constructing a list, a set or a dictionary Python provides special 
syntax called “displays”, each of them in two flavors:
either the container contents are listed explicitly, or
they are computed via a set of looping and filtering instructions, 
called a comprehension.
list_display ::=  "[" [expression_list | comprehension] "]"
A generator expression similarly represents an untyped abstract 
sequence, rather than a concrete class.

In summary: A context-manager, as an object with __enter__ and __exit__ 
methods, is not a proper component of a comprehension. For instance, 
replace "open('xxx')" in your proposal with a lock creation function. On 
the other hand, an iterable managed resource, as suggested by Mathias 
Panzenböck, works fine as a source. So it does work (as you noticed also).

Terry Jan Reedy

More information about the Python-ideas mailing list