macro FAQ

Hannu Kankaanpää hanzspam at yahoo.com.au
Tue Aug 26 10:42:10 EDT 2003


Jacek Generowicz <jacek.generowicz at cern.ch> wrote in message news:<tyf1xv8ao8q.fsf at pcepsft001.cern.ch>...
> hanzspam at yahoo.com.au (Hannu Kankaanpää) writes:
> > Obviously this was quite unsatisfactory. I ended up
> > putting the axis code in a separate class
> 
> Do you mean "function" rather than "class" ?

Actually, I did mean class. Normally I'd have

class Widget:
    def __init__(self):
        self.x = 0
        self.y = 0
        self.width = 0
        self.height = 0

    def getWidth(self):
        return self.width  # normally they wouldn't be this empty!

    def getHeight(self):
        return self.height

But by wrapping it inside a new class, I could get rid of
the duplication (partly, at least):

class Widget:
    def __init__(self):
        self.x = Widget.Axis(0, 0)
        self.y = Widget.Axis(0, 0)

    class Axis:
        def __init__(self, pos, size)
            self.pos = pos
            self.size = size

        def getSize(self):    # this is only once here now
            return self.size

While this tiny example doesn't show decrease in code
size, it shows that I have a common definition for all
"Axis"-specific code inside the appropriate Axis class.
Rest of the Widget methods would be in the widget class.
Thus self.x.getSize() instead of self.getWidth().

> > so I could
> > use them interchangeably. I.e. If I passed
> > func(self.y, self.x)
> > and then
> > func(self.x, self.y)
> > 
> > I would get the same effect on both axises. But this
> > would've been an excellent place for macros IMO
> 
> I don't see what you gain by using a macro, wrt to using a function in 
> _this_ case.

Ok, it was a bad example. I hope the code above shows
a bit more clearly what I wanted. Anyway, without the code
in the axis-class, I would've had to often say

self.x = func(self.x, self.y, self.width, self.height)
self.y = func(self.y, self.x, self.height, self.width)

Instead of

func(self.x, self.y)
func(self.y, self.x)

Which could modify the axis-specific stuff within the
func()tion. (self.x is no longer a non-modifiable number,
but a modifiable class)

> So you would not only replace whole symbols, but even fragments of
> symbols (getSize -> getHeight), and thus macically/implicitly create
> new symbols. Many people consider this bad practice.

Well, I don't, really. Like any macro that could do something
weird, it just needs to be properly understood by anyone who
wishes to read the code.

> Incidentally, in this case, having a string based code representation
> would make your job much easier than the structured representation
> which Lisp uses. In a string you'd merely do a search and replace; in
> an s-expression you would have to recursively search for all symbols
> in all sub-expressions, and then do the search and replace within the
> name of each symbol you find. 

Well, such a recursive search isn't a problem - With help from
a macro ;)

> > This way, I could've got no duplicated code but 
> > also a more intuitive interface than I currently have 
> > (to get width, one needs to type obj.x.getSize() instead 
> > of obj.getWidth()). And it's obvious this kind of "defBoth" 
> > wouldn't be added as a language level construct -- Thus 
> > macros are the only good solution. 
>  
> Cue metaclass solution ... :-)

How could metaclasses help? I'm quite inexperienced with them.
Anyway, if I take the eval route, I might as well do

defBoth('''getSize():
    return size''')

,retreive appropriate locals() from the stack and modify it
to include the new functions. I'd rather not, though :-)




More information about the Python-list mailing list