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

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

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.level += 1
    def close(self):
        assert self.level > 0
        self.level -= 1
    def done(self):
        assert self.level == 0
    def wrap(self, fun, *args):
        fun(self, *args)
    def wrapItems(self, seq, mapfunc=str):
        for item in seq:

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

def square(ctx, n, m) :
    for i in range(n):
        for j in range(m) :
            ctx.wrap(b, i, j)

ctx = Context()
ctx.wrap(square, 5, 4)
ctx.wrapItems(range(6), lambda x: str(x*x))


More information about the Python-list mailing list