More decorator rumination

  • Thread starter Scott David Daniels
  • Start date
S

Scott David Daniels

Over on comp.python.education we were discussing a (generally seen as)
misuse of decorators to build the definite integral of a function.
On thinking over the definite integral decorator, I had almost
decided that one necessary, but not sufficient, criterion for a
good decorator is that it must not change a function's arg list.
I was happy with this until just now.

I've been fighting wxPython a lot recently, and suddenly a good
working definition for a decorator came to me:

* A decorator mediates between a function and its environment.

In particular, I thought about something like:

@mousexy
def OnRightClick(self, x, y):
...

For those non-wx'ers, all GUI events come wrapped in an "event",
so all event-responding methods tend to look like:

def OnRightClick(self, event):
x = event.GetX()
y = event.GetY()

which looks like boilerplate to me. I'm wondering whether others
think this is an interesting insight into what decorators are "for,"
or they think I'm working on "the moral equivalent of a macro."

--Scott David Daniels
(e-mail address removed)
 
J

Jack Diederich

Over on comp.python.education we were discussing a (generally seen as)
misuse of decorators to build the definite integral of a function.
On thinking over the definite integral decorator, I had almost
decided that one necessary, but not sufficient, criterion for a
good decorator is that it must not change a function's arg list.
I was happy with this until just now.

I've been fighting wxPython a lot recently, and suddenly a good
working definition for a decorator came to me:

* A decorator mediates between a function and its environment.

In particular, I thought about something like:

@mousexy
def OnRightClick(self, x, y):
...

For those non-wx'ers, all GUI events come wrapped in an "event",
so all event-responding methods tend to look like:

def OnRightClick(self, event):
x = event.GetX()
y = event.GetY()

which looks like boilerplate to me. I'm wondering whether others
think this is an interesting insight into what decorators are "for,"
or they think I'm working on "the moral equivalent of a macro."

It might be plainer (and certainly more backwards compatible) to
wrap the function when setting the event handler.

EVT_BUTTON(self.frame, XRCID("ButtonName"), mousexy(self.OnRightClick))

As a matter of style & clairty, it just depends. If this is for
education I have no idea if putting the mutation in a decorator
might make it easier to explain to novices than explaining to them
that mousexy() creates a function that returns a function which is
bound to the event.

-jack

I haven't used wxPython, I just grabbed that example from
http://wiki.wxpython.org/index.cgi/XrcCheatSheet
 
S

Scott David Daniels

Jack said:
It might be plainer (and certainly more backwards compatible) to
wrap the function when setting the event handler.

EVT_BUTTON(self.frame, XRCID("ButtonName"), mousexy(self.OnRightClick))

As a matter of style & clairty, it just depends.
I think this is probably the right approach.
> If this is for education ...
I came here because it seemed to be drifting off-topic for education.

Thanks for the feedback.

-Scott David Daniels
(e-mail address removed)
 
P

Peter Otten

Scott said:
In particular, I thought about something like:

@mousexy
def OnRightClick(self, x, y):
...

You could somewhat generalize the idea -- have one argument in the wrapper
function provide the arguments missing in the wrapped one.

Here is a self-contained example, for the moment without support for keyword
arguments:

import inspect

class Event:
_x, _y, buttons = 1, 2, 3
def getX(self): return self._x
def getY(self): return self._y

_getter_for_name = dict(
x=Event.getX,
y=Event.getY,
buttons=lambda e: e.buttons
)

def event(fun):
getters = [_getter_for_name.get(arg) for arg in inspect.getargspec(fun
[0]]
def wrapped(*args):
# the Event instance must be the last argument
event = args[-1]
expanded = [get(event) for get in getters[len(args)-1:]]
args = args[:-1] + tuple(expanded)
return fun(*args)
return wrapped

class Alpha:
@event
def alpha(self, x):
print "--alpha--"
print "x =", x

@event
def beta(self, x, buttons):
print "--beta--"
print "x =", x
print "buttons =", buttons

@event
def gamma(buttons, y, x):
print "--gamma--"
print "x =", x
print "y =", y
print "buttons =", buttons

a = Alpha()
a.alpha(Event())
a.beta(Event())
gamma(Event())
# only the x argument will be set
# by the decorator (to Event().getX())
gamma("BUTTONS", "Y", Event())

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,152
Latest member
LorettaGur
Top