Adding method to a class on the fly

Discussion in 'Python' started by John Henry, Jun 22, 2007.

  1. John Henry

    John Henry Guest

    Hi list,

    I have a need to create class methods on the fly. For example, if I
    do:

    class Dummy:
    def __init__(self):
    exec '''def method_dynamic(self):\n\treturn
    self.method_static("it's me")'''
    return

    def method_static(self, text):
    print text
    return

    I like that to be the same as:

    class Dummy:
    def __init__(self):
    return

    def method_dynamic(self):
    return self.method_static("it's me")

    def method_static(self, text):
    print text
    return

    so that I can do:

    dum=Dummy.method_dynamic()

    and see "it's me" printed.

    Can that be done?

    Thanks,
     
    John Henry, Jun 22, 2007
    #1
    1. Advertising

  2. John Henry

    John Henry Guest

    Found a message on the web that says I need to use setattr to add the
    method to the class at run time. But how do I do that?

    Regards,

    On Jun 22, 12:02 pm, John Henry <> wrote:
    > Hi list,
    >
    > I have a need to create class methods on the fly. For example, if I
    > do:
    >
    > class Dummy:
    > def __init__(self):
    > exec '''def method_dynamic(self):\n\treturn
    > self.method_static("it's me")'''
    > return
    >
    > def method_static(self, text):
    > print text
    > return
    >
    > I like that to be the same as:
    >
    > class Dummy:
    > def __init__(self):
    > return
    >
    > def method_dynamic(self):
    > return self.method_static("it's me")
    >
    > def method_static(self, text):
    > print text
    > return
    >
    > so that I can do:
    >
    > dum=Dummy.method_dynamic()
    >
    > and see "it's me" printed.
    >
    > Can that be done?
    >
    > Thanks,
     
    John Henry, Jun 22, 2007
    #2
    1. Advertising

  3. John Henry

    askel Guest

    On Jun 22, 3:02 pm, John Henry <> wrote:
    > Hi list,
    >
    > I have a need to create class methods on the fly. For example, if I
    > do:
    >
    > class Dummy:
    > def __init__(self):
    > exec '''def method_dynamic(self):\n\treturn
    > self.method_static("it's me")'''
    > return
    >
    > def method_static(self, text):
    > print text
    > return
    >
    > I like that to be the same as:
    >
    > class Dummy:
    > def __init__(self):
    > return
    >
    > def method_dynamic(self):
    > return self.method_static("it's me")
    >
    > def method_static(self, text):
    > print text
    > return
    >
    > so that I can do:
    >
    > dum=Dummy.method_dynamic()
    >
    > and see "it's me" printed.
    >
    > Can that be done?
    >
    > Thanks,


    class Dummy:
    def method(self, arg):
    print arg

    def method2(self, arg):
    self.method(arg)

    Dummy.method2 = method2
    Dummy.method2('Hello, world!')
     
    askel, Jun 22, 2007
    #3
  4. John Henry

    7stud Guest

    On Jun 22, 2:24 pm, askel <> wrote:
    > class Dummy:
    > def method(self, arg):
    > print arg
    >
    > def method2(self, arg):
    > self.method(arg)
    >
    > Dummy.method2 = method2
    > Dummy.method2('Hello, world!')


    Traceback (most recent call last):
    File "test1.py", line 8, in ?
    Dummy.method2('Hello, world!')
    TypeError: unbound method method2() must be called with Dummy instance
    as first argument (got str instance
    instead)



    >I like that to be the same as:
    >
    >class Dummy:
    > def __init__(self):
    > return
    >
    > def method_dynamic(self):
    > return self.method_static("it's me")
    >
    > def method_static(self, text):
    > print text
    > return
    >
    >
    >so that I can do:
    >
    >dum=Dummy.method_dynamic()
    >
    >and see "it's me" printed.


    When are you able to see that?
     
    7stud, Jun 22, 2007
    #4
  5. John Henry

    askel Guest

    On Jun 22, 5:17 pm, 7stud <> wrote:
    > On Jun 22, 2:24 pm, askel <> wrote:
    >
    > > class Dummy:
    > > def method(self, arg):
    > > print arg

    >
    > > def method2(self, arg):
    > > self.method(arg)

    >
    > > Dummy.method2 = method2
    > > Dummy.method2('Hello, world!')

    >
    > Traceback (most recent call last):
    > File "test1.py", line 8, in ?
    > Dummy.method2('Hello, world!')
    > TypeError: unbound method method2() must be called with Dummy instance
    > as first argument (got str instance
    > instead)
    >
    >
    >
    > >I like that to be the same as:

    >
    > >class Dummy:
    > > def __init__(self):
    > > return

    >
    > > def method_dynamic(self):
    > > return self.method_static("it's me")

    >
    > > def method_static(self, text):
    > > print text
    > > return

    >
    > >so that I can do:

    >
    > >dum=Dummy.method_dynamic()

    >
    > >and see "it's me" printed.

    >
    > When are you able to see that?


    sorry, of course last line should be:
    Dummy().method2('Hello, world!')
     
    askel, Jun 22, 2007
    #5
  6. John Henry

    7stud Guest

    On Jun 22, 3:23 pm, askel <> wrote:
    > sorry, of course last line should be:
    > Dummy().method2('Hello, world!')


    ...which doesn't meet the op's requirements.
     
    7stud, Jun 22, 2007
    #6
  7. John Henry

    askel Guest

    On Jun 22, 5:17 pm, 7stud <> wrote:
    > On Jun 22, 2:24 pm, askel <> wrote:
    >
    > > class Dummy:
    > > def method(self, arg):
    > > print arg

    >
    > > def method2(self, arg):
    > > self.method(arg)

    >
    > > Dummy.method2 = method2
    > > Dummy.method2('Hello, world!')

    >
    > Traceback (most recent call last):
    > File "test1.py", line 8, in ?
    > Dummy.method2('Hello, world!')
    > TypeError: unbound method method2() must be called with Dummy instance
    > as first argument (got str instance
    > instead)
    >
    >
    >
    > >I like that to be the same as:

    >
    > >class Dummy:
    > > def __init__(self):
    > > return

    >
    > > def method_dynamic(self):
    > > return self.method_static("it's me")

    >
    > > def method_static(self, text):
    > > print text
    > > return

    >
    > >so that I can do:

    >
    > >dum=Dummy.method_dynamic()

    >
    > >and see "it's me" printed.

    >
    > When are you able to see that?


    there is no way to call instance method from static one. but in your
    case you can make something like:

    class Dummy:
    @staticmethod
    def method(arg):
    print arg

    def method2(arg):
    Dummy.method(arg)

    Dummy.method2 = staticmethod(method2)
    Dummy.method2('Hello, world!')

    - OR -

    def method2(cls, arg):
    cls.method(arg)

    Dummy.method2 = classmethod(method2)
    Dummy.method2('Hello, world!')
     
    askel, Jun 22, 2007
    #7
  8. John Henry

    John Henry Guest

    On Jun 22, 2:28 pm, askel <> wrote:
    > On Jun 22, 5:17 pm, 7stud <> wrote:
    >
    >
    >
    > > On Jun 22, 2:24 pm, askel <> wrote:

    >
    > > > class Dummy:
    > > > def method(self, arg):
    > > > print arg

    >
    > > > def method2(self, arg):
    > > > self.method(arg)

    >
    > > > Dummy.method2 = method2
    > > > Dummy.method2('Hello, world!')

    >
    > > Traceback (most recent call last):
    > > File "test1.py", line 8, in ?
    > > Dummy.method2('Hello, world!')
    > > TypeError: unbound method method2() must be called with Dummy instance
    > > as first argument (got str instance
    > > instead)

    >
    > > >I like that to be the same as:

    >
    > > >class Dummy:
    > > > def __init__(self):
    > > > return

    >
    > > > def method_dynamic(self):
    > > > return self.method_static("it's me")

    >
    > > > def method_static(self, text):
    > > > print text
    > > > return

    >
    > > >so that I can do:

    >
    > > >dum=Dummy.method_dynamic()

    >
    > > >and see "it's me" printed.

    >
    > > When are you able to see that?

    >
    > there is no way to call instance method from static one. but in your
    > case you can make something like:
    >
    > class Dummy:
    > @staticmethod
    > def method(arg):
    > print arg
    >
    > def method2(arg):
    > Dummy.method(arg)
    >
    > Dummy.method2 = staticmethod(method2)
    > Dummy.method2('Hello, world!')
    >
    > - OR -
    >
    > def method2(cls, arg):
    > cls.method(arg)
    >
    > Dummy.method2 = classmethod(method2)
    > Dummy.method2('Hello, world!')




    Thanks for the response.

    The above doesn't exactly do I what need. I was looking for a way to
    add method to a class at run time.

    What does work, is to define an entire sub-class at run time. Like:

    class DummyParent:
    def __init__(self):
    return

    def method_static(self, text):
    print text
    return

    text = "class Dummy(DummyParent):"
    text += "\n\t" + "def __init(self):"
    text += "\n\t" + "\tDummyParent.__init__(self)"
    text += "\n\t" + "def method_dynamic(self):"
    text += "\n\t" + "\tself.method_static(\"it's me\")"

    exec text

    dum=Dummy().method_dynamic()


    Thanks again.
     
    John Henry, Jun 22, 2007
    #8
  9. John Henry

    Guest

    On Jun 22, 2:44 pm, John Henry <> wrote:
    > On Jun 22, 2:28 pm, askel <> wrote:
    >


    (snipped)

    >
    > The above doesn't exactly do I what need. I was looking for a way to
    > add method to a class at run time.



    I'm not sure what you mean by this. Bind an attribute -- a method --
    to class Dummy if and only if an instance of this class is created?



    > What does work, is to define an entire sub-class at run time. Like:
    >
    > class DummyParent:
    > def __init__(self):
    > return
    >
    > def method_static(self, text):
    > print text
    > return
    >
    > text = "class Dummy(DummyParent):"
    > text += "\n\t" + "def __init(self):"
    > text += "\n\t" + "\tDummyParent.__init__(self)"
    > text += "\n\t" + "def method_dynamic(self):"
    > text += "\n\t" + "\tself.method_static(\"it's me\")"
    >
    > exec text
    >
    > dum=Dummy().method_dynamic()
    >
    > Thanks again.



    I tend to avoid exec if possible. Also, you
    seem to be a bit inexact with regard to the
    term "static".


    class Dummy(object):
    def __init__(self):
    new_method_name = 'method_dynamic'
    try:
    getattr(Dummy, new_method_name)
    except AttributeError:
    print "Creating an instance method..."
    def newf(self):
    """Something Descriptive Here"""
    return self.method_static("it's me")
    newf.__name__ = new_method_name
    setattr(Dummy, new_method_name, newf)
    def method_static(self, text):
    """I hate this name. Do not confuse this with a staticmethod;
    what you probably meant was that this is an attribute (a
    method)
    bound within the class body as opposed to elsewhere"""
    print text
    return # is this necessary?

    d1 = Dummy()
    d1.method_dynamic()
    d2 = Dummy()
    d2.method_dynamic()
    print d1.method_dynamic.im_func.__name__
    print d1.method_dynamic.im_func.__dict__
    print d1.method_dynamic.im_func.__doc__
    print d1.method_dynamic.im_func.__module__
    print d1.method_dynamic.im_self

    --
    Hope this helps,
    Steven
     
    , Jun 22, 2007
    #9
  10. John Henry

    James Stroud Guest

    7stud wrote:
    > On Jun 22, 3:23 pm, askel <> wrote:
    >
    >>sorry, of course last line should be:
    >>Dummy().method2('Hello, world!')

    >
    >
    > ..which doesn't meet the op's requirements.
    >


    Which were contradictory.
     
    James Stroud, Jun 23, 2007
    #10
  11. John Henry

    James Stroud Guest

    John Henry wrote:
    > Hi list,
    >
    > I have a need to create class methods on the fly. For example, if I
    > do:
    >
    > class Dummy:
    > def __init__(self):
    > exec '''def method_dynamic(self):\n\treturn
    > self.method_static("it's me")'''
    > return
    >
    > def method_static(self, text):
    > print text
    > return


    Where is the text for the exec statement coming from? A file? User
    input? What you are doing above makes absolutely no sense. You confuse
    everyone who attempts to understand what you want to do with the above
    because no one in his right mind would do anything like it.

    > I like that to be the same as:
    >
    > class Dummy:
    > def __init__(self):
    > return
    >
    > def method_dynamic(self):
    > return self.method_static("it's me")
    >
    > def method_static(self, text):
    > print text
    > return


    Are you looking to fill in text and create new methods for Dummy based
    on the text and method_static() such that these will become true
    instance methods for instances of Dummy?

    def add_dynamic(cls_name, f, name, *args, **kwargs):
    cls = globals()[cls_name]
    def _f(self):
    return getattr(cls, f)(self, *args, **kwargs)
    setattr(cls, name, _f)

    e.g.

    py> class Dummy:
    .... def method_static(self, text):
    .... print text
    .... return
    ....
    py> def add_dynamic(cls_name, f, name, *args, **kwargs):
    .... cls = globals()[cls_name]
    .... def _f(self):
    .... return getattr(cls, f)(self, *args, **kwargs)
    .... setattr(cls, name, _f)
    ....
    py> add_dynamic('Dummy', 'method_static', 'method_dynamic', 'abc xyz')
    py> Dummy.method_dynamic
    <unbound method Dummy._f>
    py> d = Dummy()
    py> d.method_dynamic()
    abc xyz

    Note that this is "completely dynamic" in that all arguments to
    add_dynamic() are strings. This may or may not be what you want--in such
    a case, you will want to study the code to see how it works and fix it
    yourself. Note also that methods added to classes after instances are
    made will be available to said instances:


    py> add_dynamic('Dummy', 'method_static', 'method2_dynamic', 'asdf jkl')
    py> d.method2_dynamic()
    asdf jkl


    > so that I can do:
    >
    > dum=Dummy.method_dynamic()


    Here you confuse everyone. This last line is not the same as you
    describe in the above example. Here you imply the dynamic creation of a
    "static method" (not a "method_static"--don't be confused by the names
    you invent as they may already have a meaning to everyone else). Static
    methods are different from unbound class methods that are later bound to
    instances of a class upon instantiation.

    Here is an analagous solution for static methods:


    def add_static(cls_name, f, name, *args, **kwargs):
    cls = globals()[cls_name]
    def _f():
    return getattr(cls, f)(*args, **kwargs)
    setattr(cls, name, staticmethod(_f))

    class Dummy:
    @staticmethod
    def method_static(text):
    print text


    e.g.:


    py> def add_static(cls_name, f, name, *args, **kwargs):
    .... cls = globals()[cls_name]
    .... def _f():
    .... return getattr(cls, f)(*args, **kwargs)
    .... setattr(cls, name, staticmethod(_f))
    ....
    py> class Dummy:
    .... @staticmethod
    .... def method_static(text):
    .... print text
    ....
    py> add_static('Dummy', 'method_static', 'real_static', 'aaa bbb')
    py> Dummy.real_static
    <function _f at 0x406bf684>
    py> Dummy.real_static()
    aaa bbb



    Again, this will do what you want, but if it doesn't do *exactly* what
    you want, you need to study and modify the code. Also, creating static
    methods from unbound methods requires trickery. If this is what you
    want, you should be very clear about it.


    > and see "it's me" printed.
    >
    > Can that be done?



    Yes. Anything is possible with python. That's why I use it.


    James
     
    James Stroud, Jun 23, 2007
    #11
  12. On Fri, 22 Jun 2007 14:44:54 -0700, John Henry wrote:

    > The above doesn't exactly do I what need. I was looking for a way to
    > add method to a class at run time.
    >
    > What does work, is to define an entire sub-class at run time. Like:
    >
    > class DummyParent:
    > def __init__(self):
    > return
    >
    > def method_static(self, text):
    > print text
    > return
    >
    > text = "class Dummy(DummyParent):"
    > text += "\n\t" + "def __init(self):"
    > text += "\n\t" + "\tDummyParent.__init__(self)"
    > text += "\n\t" + "def method_dynamic(self):"
    > text += "\n\t" + "\tself.method_static(\"it's me\")"
    >
    > exec text


    (By the way, you misspelled __init__.)

    The correct way to add methods to an instance is with the
    instancemethod() function.


    class Parrot:
    def __init__(self):
    import new
    # define a function
    def method_dynamic(self, *args):
    args.insert(0, "hello, it's me!")
    return self.method_static(*args)
    # convert it into an instance method
    method = new.instancemethod(function, self, self.__class__)
    # add it to self
    self.method_dynamic = method
    def method_static(self, text):
    return text


    And here is how I use it:

    >>> p = Parrot()
    >>> p.method_dynamic() # call from an instance

    "it's me"
    >>> Parrot.method_dynamic # does it exist in the class?

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    AttributeError: class Parrot has no attribute 'method_dynamic'


    BUT, having said all that, are you sure this is what you want to do? This
    is probably a better way to get the same results:

    class Parrot:
    def __init__(self):
    self.text = "hello, it's me!"
    def method_dynamic(self):
    return self.method_static(self.text)
    def method_static(self, text):
    return text


    Earlier in the thread, you said you wanted a CLASS method, which is very
    different. You can use the classmethod built-in function (no need to
    import new) to create class methods:

    class Parrot:
    def method_dynamic(cls):
    return cls.method_static(cls(), "hello it's me")
    # or alternatively cls().method_static("hello it's me")
    method_dynamic = classmethod(method_dynamic)
    def method_static(self, text):
    return text

    Note: if you are using recent versions of Python, instead of saying
    "method = classmethod(method)" AFTER the block, you can use a decorator
    before the block.

    Making method_dynamic a class method and calling an instance method is not
    a good way of doing things, since the class method has to create a new
    instance before calling method_static, only to throw it away afterwards.
    That is wasteful and could be very expensive.

    A better way is to change your class so that method_static is a class
    method too, especially since it doesn't use self:

    class Parrot:
    @classmethod
    def method_dynamic(cls):
    return cls.method_static("hello it's me")
    @classmethod
    def method_static(cls, text):
    return text

    (Actually, since method_static doesn't even use the class, you could use
    staticmethod instead of classmethod. Remember to remove the "cls" argument.)

    Hope this helps,


    --
    Steven.
     
    Steven D'Aprano, Jun 23, 2007
    #12
  13. John Henry

    John Henry Guest

    On Jun 22, 7:36 pm, Steven D'Aprano
    <> wrote:
    > On Fri, 22 Jun 2007 14:44:54 -0700, John Henry wrote:
    > > The above doesn't exactly do I what need. I was looking for a way to
    > > add method to a class at run time.

    >
    > > What does work, is to define an entire sub-class at run time. Like:

    >
    > > class DummyParent:
    > > def __init__(self):
    > > return

    >
    > > def method_static(self, text):
    > > print text
    > > return

    >
    > > text = "class Dummy(DummyParent):"
    > > text += "\n\t" + "def __init(self):"
    > > text += "\n\t" + "\tDummyParent.__init__(self)"
    > > text += "\n\t" + "def method_dynamic(self):"
    > > text += "\n\t" + "\tself.method_static(\"it's me\")"

    >
    > > exec text

    >
    > (By the way, you misspelled __init__.)
    >
    > The correct way to add methods to an instance is with the
    > instancemethod() function.
    >
    > class Parrot:
    > def __init__(self):
    > import new
    > # define a function
    > def method_dynamic(self, *args):
    > args.insert(0, "hello, it's me!")
    > return self.method_static(*args)
    > # convert it into an instance method
    > method = new.instancemethod(function, self, self.__class__)
    > # add it to self
    > self.method_dynamic = method
    > def method_static(self, text):
    > return text
    >
    > And here is how I use it:
    >
    > >>> p = Parrot()
    > >>> p.method_dynamic() # call from an instance

    > "it's me"
    > >>> Parrot.method_dynamic # does it exist in the class?

    >
    > Traceback (most recent call last):
    > File "<stdin>", line 1, in ?
    > AttributeError: class Parrot has no attribute 'method_dynamic'
    >
    > BUT, having said all that, are you sure this is what you want to do? This
    > is probably a better way to get the same results:
    >
    > class Parrot:
    > def __init__(self):
    > self.text = "hello, it's me!"
    > def method_dynamic(self):
    > return self.method_static(self.text)
    > def method_static(self, text):
    > return text
    >
    > Earlier in the thread, you said you wanted a CLASS method, which is very
    > different. You can use the classmethod built-in function (no need to
    > import new) to create class methods:
    >
    > class Parrot:
    > def method_dynamic(cls):
    > return cls.method_static(cls(), "hello it's me")
    > # or alternatively cls().method_static("hello it's me")
    > method_dynamic = classmethod(method_dynamic)
    > def method_static(self, text):
    > return text
    >
    > Note: if you are using recent versions of Python, instead of saying
    > "method = classmethod(method)" AFTER the block, you can use a decorator
    > before the block.
    >
    > Making method_dynamic a class method and calling an instance method is not
    > a good way of doing things, since the class method has to create a new
    > instance before calling method_static, only to throw it away afterwards.
    > That is wasteful and could be very expensive.
    >
    > A better way is to change your class so that method_static is a class
    > method too, especially since it doesn't use self:
    >
    > class Parrot:
    > @classmethod
    > def method_dynamic(cls):
    > return cls.method_static("hello it's me")
    > @classmethod
    > def method_static(cls, text):
    > return text
    >
    > (Actually, since method_static doesn't even use the class, you could use
    > staticmethod instead of classmethod. Remember to remove the "cls" argument.)
    >
    > Hope this helps,
    >
    > --
    > Steven.



    Thanks everybody for your responses. I know my terminology isn't
    quite exact. Hopefully that didn't confuse you too much. I used that
    example hoping to simplify the question. As you'll see below, it
    takes more if I have to explain the entire story.

    Of all the answers, I think the new.instancemethod is most
    appropriate. I'll try to explain:

    With a PythonCard application, if you want to have a button, normally
    you use the layout editor which creates a dictionary representing the
    button, and you would have a function for each of the events it has to
    handle. For instance, a simple one button ap might look like this:

    #!/usr/bin/python

    """
    __version__ = "$Revision: 1.6 $"
    __date__ = "$Date: 2004/08/17 19:46:06 $"
    """

    from PythonCard import model

    rsrc = {'application':{'type':'Application',
    'name':'Minimal',
    'backgrounds': [
    {'type':'Background',
    'name':'bgMin',
    'title':'Minimal PythonCard Application',
    'size':(200, 100),
    'components': [
    {'type':'Button', 'name':'Button1', 'position':(5, 35),
    'label':'Button1'},
    ] # end components
    } # end background
    ] # end backgrounds
    } }

    class Minimal(model.Background):

    def on_initialize(self, event):
    pass
    def on_Button1_mouseClick(self, event):
    print "Clicked Button1"


    if __name__ == '__main__':
    app = model.Application(Minimal, None, rsrc)
    app.MainLoop()


    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? With the
    application I have to come up with, I have a tree on the left, and
    then depending which branch the user clicks, I have to create various
    controls on the right. So, it becomes some what of a nightmare for me
    (since I have lots of branches and they are all quite different).
    Each time the use click a branch, I have to create the buttons, data
    entry-fields, and so forth on the fly - along with all of the
    functions to handle them.

    This is what I came up with so far (before reading your messages):

    #!/usr/bin/python

    """
    __version__ = "$Revision: 1.6 $"
    __date__ = "$Date: 2004/08/17 19:46:06 $"
    """

    from PythonCard import model

    rsrc = {'application':{'type':'Application',
    'name':'Minimal',
    'backgrounds': [
    {'type':'Background',
    'name':'bgMin',
    'title':'Minimal PythonCard Application',
    'size':(200, 300),
    'components': [

    ] # end components
    } # end background
    ] # end backgrounds
    } }

    class Minimal(model.Background):
    def on_initialize(self, event):
    return

    nButtons = 7

    text="class MinimalChild(Minimal):"
    text += "\n\t" + "def on_initialize(self, event):"
    text += "\n\t" + "\tMinimal.on_initialize(self,event)"
    for iButton in xrange(nButtons):
    name = "Button"+str(iButton+1)
    text += "\n\t" + "\tself.components['"+name+"'] = {" +\
    "'type':'Button', " +\
    "'name':'"+name+"', " +\
    "'label':'"+name+"', "+\
    "'position':(5, "+str(35+iButton*30)+")}"
    if iButton!=nButtons-1:
    text += "\n\t" + "def on_"+name+"_mouseClick(self, event):"
    exec(text)

    if __name__ == '__main__':
    app = model.Application(MinimalChild, None, rsrc)
    app.MainLoop()


    With this approach, each time I click a button, a new one gets
    created, along with a new handler for mouseClick of that new button.

    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.

    Regards,
     
    John Henry, Jun 23, 2007
    #13
  14. 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.
     
    Steven D'Aprano, Jun 23, 2007
    #14
  15. John Henry

    John Henry Guest

    >
    > > 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:
    >

    <snip>

    Steven,

    May be I didn't explain it clearly: the PythonCard package expects to
    see a function by the name of on_Button1_mouseClick. I don't do
    anything to register the callback function. The package assumes that
    there is a function by that name whenever I create a button named
    Button1. So, if I don't use exec, how can I create a function by that
    exact name?
     
    John Henry, Jun 23, 2007
    #15
  16. On Sat, 23 Jun 2007 09:06:36 -0700, John Henry wrote:

    >>
    >> > 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:
    >>

    > <snip>
    >
    > Steven,
    >
    > May be I didn't explain it clearly: the PythonCard package expects to
    > see a function by the name of on_Button1_mouseClick. I don't do
    > anything to register the callback function. The package assumes that
    > there is a function by that name whenever I create a button named
    > Button1. So, if I don't use exec, how can I create a function by that
    > exact name?



    def mouseclick_factory(name):
    def function(self, event):
    print "You clicked '%s'." % name
    function.name = "on_%s_mouseClick" % name
    return function



    class Parrot:
    def __init__(self, name):
    function = mouseclick_factory(name) # as before
    method = new.instancemethod(function, self, self.__class__)
    setattr(self, function.name, method)


    And here it is in action:

    >>> p = Parrot("Button1")
    >>> p.on_Button1_mouseClick("event")

    You clicked 'Button1'.



    --
    Steven.
     
    Steven D'Aprano, Jun 23, 2007
    #16
  17. John Henry

    John Henry Guest

    On Jun 23, 10:56 am, Steven D'Aprano
    <> wrote:
    > On Sat, 23 Jun 2007 09:06:36 -0700, John Henry wrote:
    >
    > >> > 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:

    >
    > > <snip>

    >
    > > Steven,

    >
    > > May be I didn't explain it clearly: the PythonCard package expects to
    > > see a function by the name of on_Button1_mouseClick. I don't do
    > > anything to register the callback function. The package assumes that
    > > there is a function by that name whenever I create a button named
    > > Button1. So, if I don't use exec, how can I create a function by that
    > > exact name?

    >
    > def mouseclick_factory(name):
    > def function(self, event):
    > print "You clicked '%s'." % name
    > function.name = "on_%s_mouseClick" % name
    > return function
    >
    > class Parrot:
    > def __init__(self, name):
    > function = mouseclick_factory(name) # as before
    > method = new.instancemethod(function, self, self.__class__)
    > setattr(self, function.name, method)
    >
    > And here it is in action:
    >
    > >>> p = Parrot("Button1")
    > >>> p.on_Button1_mouseClick("event")

    >
    > You clicked 'Button1'.
    >
    > --
    > Steven.



    Thank you. I think that should work perfectly. By using
    mouseclick_factory, you've avoided using exec and result in a much
    more readable code. The part I really didn't know how is the use of
    the new.instancemethod followed by setattr. I'll go try it now.

    Thanks again.
     
    John Henry, Jun 23, 2007
    #17
  18. John Henry

    John Henry Guest

    On Jun 23, 10:56 am, Steven D'Aprano
    <> wrote:
    > On Sat, 23 Jun 2007 09:06:36 -0700, John Henry wrote:
    >
    > >> > 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:

    >
    > > <snip>

    >
    > > Steven,

    >
    > > May be I didn't explain it clearly: the PythonCard package expects to
    > > see a function by the name of on_Button1_mouseClick. I don't do
    > > anything to register the callback function. The package assumes that
    > > there is a function by that name whenever I create a button named
    > > Button1. So, if I don't use exec, how can I create a function by that
    > > exact name?

    >
    > def mouseclick_factory(name):
    > def function(self, event):
    > print "You clicked '%s'." % name
    > function.name = "on_%s_mouseClick" % name
    > return function
    >
    > class Parrot:
    > def __init__(self, name):
    > function = mouseclick_factory(name) # as before
    > method = new.instancemethod(function, self, self.__class__)
    > setattr(self, function.name, method)
    >
    > And here it is in action:
    >
    > >>> p = Parrot("Button1")
    > >>> p.on_Button1_mouseClick("event")

    >
    > You clicked 'Button1'.
    >
    > --
    > Steven.



    Wouldn't it be nice if it works right away? :=)

    I tried the above method and this is what I have:

    #!/usr/bin/python

    """
    __version__ = "$Revision: 1.6 $"
    __date__ = "$Date: 2004/08/17 19:46:06 $"
    """

    import new

    from PythonCard import model

    rsrc = {'application':{'type':'Application',
    'name':'Minimal',
    'backgrounds': [
    {'type':'Background',
    'name':'bgMin',
    'title':'Minimal PythonCard Application',
    'size':(200, 300),
    'components': [

    ] # end components
    } # end background
    ] # end backgrounds
    } }

    def mouseclick_factory(parent, name):
    id_num=int(name[-1:])
    parent.components[name] = {'type':'Button',
    'name':name,
    'label':name,
    'position':(5, 5+id_num*30),
    'text':name}
    def function(self, event):
    print "You clicked '%s'." % name
    function.name = "on_%s_mouseClick" % name
    return function

    class Minimal(model.Background):
    def on_initialize(self, event):
    self.components['field1'] =
    {'type':'TextField','name':'field1','position':(5, 5),'size':(150,
    -1),'text':'Hello PythonCard'}
    name = "Button1"
    function = mouseclick_factory(self, name) # as before
    method = new.instancemethod(function, self, self.__class__)
    setattr(self, function.name, method)

    if __name__ == '__main__':
    app = model.Application(Minimal, None, rsrc)
    app.MainLoop()

    When I click on the button, nothing happens. However, if I call the
    function directly (like right after the setattr line:

    self.on_Button1_mouseClick(event)

    it works fine but PythonCard isn't calling this function when I
    clicked on the button.
     
    John Henry, Jun 23, 2007
    #18
  19. On Sat, 23 Jun 2007 12:31:39 -0700, John Henry wrote:

    > it works fine but PythonCard isn't calling this function when I
    > clicked on the button.



    I think you need to take this question onto a PythonCard list. I have no
    idea how PythonCard decides which method to call.


    --
    Steven.
     
    Steven D'Aprano, Jun 24, 2007
    #19
  20. John Henry

    John Henry Guest

    On Jun 23, 6:24 pm, Steven D'Aprano
    <> wrote:
    > On Sat, 23 Jun 2007 12:31:39 -0700, John Henry wrote:
    > > it works fine but PythonCard isn't calling this function when I
    > > clicked on the button.

    >
    > I think you need to take this question onto a PythonCard list. I have no
    > idea how PythonCard decides which method to call.
    >
    > --
    > Steven.



    I did. I am not sure I'll get an answer though.

    Thanks for the help.
     
    John Henry, Jun 24, 2007
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Harlin Seritt

    Adding an option on the fly (Tkinter)

    Harlin Seritt, Mar 6, 2005, in forum: Python
    Replies:
    3
    Views:
    300
    Diez B. Roggisch
    Mar 6, 2005
  2. marekw2143
    Replies:
    3
    Views:
    1,366
    marekw2143
    Jul 25, 2009
  3. Raj Singh
    Replies:
    2
    Views:
    200
    Rick DeNatale
    May 29, 2008
  4. Gabriel Saravia

    Adding a =~ method to the Method class

    Gabriel Saravia, May 7, 2009, in forum: Ruby
    Replies:
    4
    Views:
    211
    Gabriel Saravia
    May 7, 2009
  5. Brian

    Fly outmenu on the fly

    Brian, Apr 8, 2005, in forum: Javascript
    Replies:
    0
    Views:
    111
    Brian
    Apr 8, 2005
Loading...

Share This Page