[Python-Dev] anonymous blocks

Guido van Rossum gvanrossum at gmail.com
Fri Apr 22 01:40:28 CEST 2005


I've been thinking about this a lot, but haven't made much
progess. Here's a brain dump.

I've been thinking about integrating PEP 325 (Resource-Release Support
for Generators) into the for-loop code, so that you could replace

    the_lock.acquire()
    try:
        BODY
    finally:
        the_lock.release()

with

    for dummy in synchronized(the_lock):
        BODY

or perhaps even (making "for VAR" optional in the for-loop syntax)
with

    in synchronized(the_lock):
        BODY

Then synchronized() could be written cleanly as follows:

    def synchronized(lock):
        lock.acquire()
        try:
            yield None
        finally:
            lock.release()

But then every for-loop would have to contain an extra try-finally
clause; the translation of

    for VAR in EXPR:
        BODY

would become

    __it = iter(EXPR)
    try:
        while True:
            try:
                VAR = __it.next()
            except StopIteration:
                break
            BODY
    finally:
        if hasattr(__it, "close"):
            __it.close()

which I don't particularly like: most for-loops DON'T need this, since
they don't use a generator but some other form of iterator, or even if
they use a generator, not all generators have a try/finally loop.  But
the bytecode compiler can't know that, so it will always have to
generate this code.  It also changes the semantics of using a
generator in a for-loop slightly: if you break out of the for-loop
before the generator is exhausted you will still get the close() call.

It's also a bit funny to see this approach used with the only other
use case for try/finally we've looked at, which requires passing a
variable into the block: the "with_file" use case.  We now can write
with_file as a nice and clean generator:

    def with_file(filename):
        f = open(filename)
        try:
            yield f
        finally:
            f.close()

but the use looks very odd because it is syntactically a for-loop but
there's only one iteration:

    for f in with_file("/etc/passwd"):
        for line in f:
            print line[:line.find(":")]

Seeing this example makes me cringe -- why two nested for loops to
loop over the lines of one file???

So I think that this is probably not the right thing to pursue, and we
might be better off with something along the lines of PEP 310.  The
authors of PEP 310 agree; under Open Issues they wrote:

    There are some simiralities in concept between 'with ...' blocks
    and generators, which have led to proposals that for loops could
    implement the with block functionality[3].  While neat on some
    levels, we think that for loops should stick to being loops.

(Footnote [3] references the tread that originated PEP 325.)

Perhaps the most important lesson we've learned in this thread is that
the 'with' keyword proposed in PEP 310 is redundant -- the syntax
could just be

    [VAR '=']* EXPR ':'
        BODY

IOW the regular assignment / expression statement gets an optional
colon-plus-suite at the end.

So now let's assume we accept PEP 310 with this change.  Does this
leave any use cases for anonymous blocks uncovered?  Ruby's each()
pattern is covered by generators; personally I prefer Python's

    for var in seq: ...

over Ruby's much-touted

    seq.each() {|var| ...}

The try/finally use case is covered by PEP 310. (If you want to
combine this with a for-loop in a single operation, you'll need PEP
325.)

The use cases where the block actually returns a value are probably
callbacks for things like sort() or map(); I have to admit that I'd
rather keep lambda for these (and use named functions for longer
blocks) than introduce an anonymous block syntax that can return
values!  I also note that if you *already* have a comparison function,
Ruby's Array sort method doesn't let you pass it in as a function
argument; you have to give it a block that calls the comparison
function, because blocks are not the same as callables (and I'm not
sure that Ruby even *has* callables -- everything seems to be a
block).

My tentative conclusion remains: Python doesn't need Ruby blocks.
Brian Sabbey ought to come up with more examples rather than arguments
why his preferred syntax and semantics are best.

--Guido van Rossum (home page: http://www.python.org/~guido/)


More information about the Python-Dev mailing list