Macros in Python? (PEP 310)

Alex Martelli aleax at aleax.it
Sat Apr 12 12:18:04 CEST 2003


A. Lloyd Flanagan wrote:
   ...
> I think you are making this a lot harder than it needs to be.
> Remember that function definitions are first class objects in python:
> 
> def y(a, b):
>     " a simple function to do nothing significant
>     return a + b
> 
> _y = y  #save defn. of y
> 
> def x(a, b):
>     # function to wrap stuff around y
>     print "hi"
>     c = _y(a, b)
>     print "bye"
>     return c
> 
> y = x  #rebind y so it looks identical to caller

This is special-purpose code wrapping a function y callable
with two arguments, and no other -- to me, it's like answering
a question such as "can I write a Python function to sum two
numbers" with the answer "sure, 23+190".  It's not hard at all
to make a slightly umore general wrapper:

def xify(func):
    def xified(*args, **kwds):
        print "hi"
        result = func(*args, **kwds)
        print "bye"
        return result
    return xified

now,
y = xify(y)
gives you the special-case you hardcoded above, but the
(modest) generalization should be OK.

Why is this generalization still modest:
    -- func must be coded as a function (no way to pass
       xify anonymous blocks of inline code except with the
       feeble Python lambda which only allows an expression)
    -- the prefix and postfix that wrap func are hard-coded
    -- the postfix doesn't happen if func raises an exception
       rather than returning normally

Point 1 is just too hard to fix -- Python just doesn't like
treating anonymous blocks of code as first-class objects.
Point 2 is easy to ameliorate, and point 3 is trivial, e.g.:

def mkprint(something):
    def print_(): print something
    return print_

def wrapfunc(func, pre=mkprint("hi"), post=mkprint("bye")):
    def wrapped(*args, **kwds):
        pre()
        try: return func(*args, **kwds)
        finally: post()
    return wrapped

If you do this sort of thing a lot you'll no doubt want
lots of little helpers such as

def curry_to_0(func, *args, **kwds):
    def curried(): return func(*args, **kwds)
    return curried

def curry_to_anyp(func, *args, **kwds):
    def curried(**more): return func(*(args+more), **kwds)
    return curried

etc, etc (or, many of the little helpers could be grouped
into one larger helper, probably a class rather than a
closure).  But it's gonna be fussy, due to the inability
to pass anonymous blocks of inline code and the distinction
between expressions and statements.


Alex





More information about the Python-list mailing list