Adding method to a class on the fly

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Fri Jun 22 22:36:40 EDT 2007


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.




More information about the Python-list mailing list