lesscode.org


Ruby-colored Blocks in Python  

By Ryan Tomayko under Python, Ruby on 12. July 2005

Ruby-styled blocks in Python have been something to be a bit anxious about. After spending even a little time working with Ruby, it’s really hard to get the idioms that blocks facilitate out of your head. Dealing with certain types of problems with coroutines just feels right:

File.open("bla.txt") do |file|
   file.read()
end

With the equivalent Python being roughly:

file = open('foo.txt')
try:
    file.read()
finally:
    file.close()

Eewww. What attracted me to Python was its ability to make code read very closely to its intent as it would be described by a human. Resource aquisition/release is one of those things that I’d rather not have to read about when I’m trying to extract the essence of what a piece of code is doing.

Of course, you can get close in most cases by using normal callable passing…

def with_open_file(file, block):
    try:
        block(file)
    finally:
        file.close()

def do_stuff(file):
    file.read()

with_open_file(open('bla.txt'), do_stuff)

… but the Ruby block syntax reads better to these eyes and it seems I’m not the only one. A few weeks ago, Guido had this to say about the block style in relation to recent PEP activity:

That was all before I (re-)discovered yield-expressions (in Ruby!), and mostly in response to the most recent version of PEP 288, with its problem of accessing the generator instance. I now strongly feel that g.next(EXPR) and yield-expressions are the way to go.

So I’ve been following the succession of PEPs that have led us to PEP-342, Coroutines via Enhanced Generators, and PEP-343, Anonymous Block Redux and Generator Enahncements, with great interest.

It looks like there’s a decent chance that we’ll be able to stuff like this in Python 2.5:

 @with_template
 def opening(filename, mode="r"):
     file = open(filename, mode)
     try:
         yield file
     finally:
         file.close()

 with opening("/etc/passwd") as file:
     file.read()

This is accomplished not by adding an implicitly passed block construct like Ruby’s &block, but by adding a basic message passing protocol for generators. Generators will have two new methods: send and throw. The important one here is throw, which tells the generator to raise an Exception at its suspension point. All of this is hidden behind the implementation of with_template (sample implementation in PEP-342).

What I’m interested in understanding more fully is how the PEP proposed enhancements to generators will work in iterative cases or whether that’s planned at all. The examples seem targeted toward resource aquisition/release (which is fine, there’s definitely a strong set of use cases in that area). But I’m interested in understanding how cases similar to Ruby’s IO.foreach will be handled:

IO.foreach("testfile") { |line| puts line }

Note that this is different from Python’s…

for line in open('testfile').readlines():
    print line

… for a few reasons. First, resource aquisition/release is handled within IO.foreach where in the preceding Python snippet, it isn’t really handled at all. Second, IO.foreach reads the file iteratively, calling the block each time, where as an approach using Python generators, such as follows…

def lines(filename):
    f = open(filename)
    try:
        line = f.readline()
        while line:
           yield line
           line = f.readline()
    finally:
        f.close()

for line in lines('foo.txt'):
    raise Exception("this doesn't work")

… falls apart because the exception raised in the iterating block is not automatically signaled back to the generator. There’s no guarantee that the finally block will execute when the iterating block exits.

I have a feeling there’s some aspect of this that I’m not fully grasping. Perhaps Phillip (PING) or someone else with a good understanding of the proposed generator enhancements can stop by and comment; inquiring minds want to know…

3 Responses to “Ruby-colored Blocks in Python”

  1. Anonymous:

    with opening(”/etc/passwd”) as file:
    for line in file:

    the file will be eventually closed. ‘for’ and ‘with’ are orthogonal for worse or better.

    comment at 16. July 2005

  2. Ryan Tomayko:

    In many cases, it is important that before and after code be executed immediately before entering and immediately after exiting a for loop. The with/for example you gave would accomplish that but it would be nice to condense those two constructs down into one, no? One proposal was to check if the object referenced in the for has __enter__ and __exit__ and to call them appropriately.

    comment at 16. July 2005

  3. Web Standards Planet:

    [WSG Announce] Some links for light reading (17/7/05)

    WaSP Interviews Dr. Vito Evola on a course in Web standards offered at the
    University of Palermo, Italy

    SiteMorse: Not making friends or influencing people

    How to style a restaurant menu with CSS:

    trackback at 17. July 2005

Leave a Reply

Note: None of this information is required but leaving a Name and URL is much appreciated. You can also register to have this stuff remembered.

Your comment can be previewed here.


Markdown: use the force, Luke.