Adding method to a class on the fly

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Sat Jun 23 16:42:47 CEST 2007


On Sat, 23 Jun 2007 00:02:09 -0700, John Henry wrote:

[snip]
> Notice that the event handler for mouseClick to Button1 is done via
> the function on_Button1_mouseClick.  This is very simple and works
> great - until you try to create the button on the fly.
> 
> Creating the button itself is no problem.  You simply do a:
> 
>         self.components['Button1'] = {'type':'Button',
>                                      'name':'Button1',
>                                      'position':(5, 35),
>                                      'label':'Button1'}
> 
> But then how do I create the on_Button1_mouseClick function?

That depends on what it is supposed to do, but in general you want a
factory function -- a function that returns functions. Here's a simple
example:

def mouseclick_factory(arg):
    def on_mouseClick(self, event):
        print "You clicked '%s'." % arg
    return on_mouseClick

func1 = mouseclick_factory("Button 1")
func2 = mouseclick_factory("this button")
func3 = mouseclick_factory("something")


Now let's try them out, faking the "self" and "event" parameters:


>>> func1(None, None)
You clicked 'Button 1'.
>>> func2(None, None)
You clicked 'this button'.
>>> func3(None, None)
You clicked 'something'.


Obviously in a real application, self and event are important and can't be
faked with None.

Now, there are two ways of using that factory function in a class. Here
is an example of both.

class Parrot:
    def __init__(self, arg):
        self.bar = mouseclick_factory(arg)
    foo = mouseclick_factory("Foo")

p = Parrot("bar")

If you do it like this, there is a slight Gotcha to watch out for: as
provided, foo is an instance method (and so has the self argument
supplied automatically) but bar is not (and so needs the self argument to
be supplied manually.

>>> p.foo(None) # fake event argument
You clicked 'Foo'.
>>> p.bar(p, None) # self and fake event arguments
You clicked 'bar'.

If this is a problem -- and believe me, it will be -- you can use
new.instancemethod to convert bar.


[snip]

> Now, knowing the new.instancemethod way, may be I can simplify the
> above somewhat and improve the efficiencies but I still don't see how
> one can do it without using the exec function.

Rule 1:
Never use exec.

Exception for experts:
If you know enough to never need exec, you can use it.

Rule 1 is actually not quite true, but as an approximation to the truth,
it is quite good.



-- 
Steven.




More information about the Python-list mailing list