On python syntax...

Peter Otten __peter__ at web.de
Mon Nov 10 16:14:01 CET 2003


Steve H wrote:

> I'm writing a library in which I have a stream object supporting a
> Write mehod, and I require to provide support for writing arbitrary
> strings in arbitrary nested contexts.  A simplified example solution
> might look like this.
> 
> --
> # Simplified matching marker calls.
> def Open(s) :
> s.write("(")
> def Close(s) :
> s.write(")")
> 
> #Main body
> def Square(s) :
> s.Open()
> for i in range(100) :
> s.open
> for j in range(100) :
> s.write("{%s,%s}"%i,j)
> s.close
> s.close
> --
> 
> While this solution would work, I'm less than happy that the
> programmer is left to ensure calls to Open are matched with calls to
> close.  It is significant to note that every call to open is at the
> beginning of a nested context, and every call to close is at the
> corresponing end of the same nested context.
> 
> Having glanced through the python manual I notice that the C++ trick
> of using an object with a destructor to manage this sort of behaviour
> is inappropriate for a phython script (as __del__ may be called at any
> time once the ref-count for an object is 0.)  I wonder, is there a
> better approach to this problem than the solution above (maybe using
> lambda functions?)  I'd like a main body of the following form to
> generate the same result:
> 
> def Square(s) :
> ManageContext(s)
> for i in range(100) :
> ManageContext(s)
> for j in range(100) :
> s.write("{%s,%s}"%i,j)
> 
> Any suggestions?

Below is a Context class with a random mix of ways to tackle your problem.
Instead of

ctx.open()
yourfunc(ctx, arg1, ..., argN)
ctx.close()

just write

ctx.wrap(yourfunc, arg1, ..., argN)

While in doubt about the wrap() and wrapItems() methods, I think that the
level-counting approach is worth consideration. Store the level at the
begin of a function and assert that it has not changed at the end, putting
it into a try ... finally statement for functions with more then one exit
point (or just query it with your debugger).

class Context:
    def __init__(self, write=None):
        if write is None:
            import sys
            write = sys.stdout.write
        self.write = write
        self.level = 0
    def open(self):
        self.write("(")
        self.level += 1
    def close(self):
        assert self.level > 0
        self.write(")")
        self.level -= 1
    def done(self):
        assert self.level == 0
    def wrap(self, fun, *args):
        self.open()
        fun(self, *args)
        self.close()
    def wrapItems(self, seq, mapfunc=str):
        for item in seq:
            self.open()
            self.write(mapfunc(item))
            self.close()

def b(ctx, i, j):
    ctx.write("%s %s" % (i, j))

def square(ctx, n, m) :
    for i in range(n):
        ctx.open()
        for j in range(m) :
            ctx.wrap(b, i, j)
        ctx.close()
        ctx.write("\n")

ctx = Context()
ctx.wrap(square, 5, 4)
ctx.write("\n")
ctx.wrapItems(range(6), lambda x: str(x*x))
ctx.done()

Peter




More information about the Python-list mailing list