开发者

Overload a method with a function at runtime

OK, I'll admit upfront this is a mega kludge and that I could definately implement this better.开发者_Python百科 It's only morbid curiosity that's driving me to find out how I could do this.

class SomeClass(object):
    def __init__(self):
        def __(self, arg):
            self.doStuff(arg)
        self.overLoaded = __
    def doStuff(self, string):
        print string

SomeClass().overLoaded("test string")

This returns a parameter error because I'm only supplying overLoaded() with one argument instead of two. Is there some magic to tell the interpreter that it's now a method of a class (I tried decorating it with @classmethod, I always understood this to be it's purpose??)


Don't worry about the self parameter, the function already has that from local scope.

class SomeClass(object):
    def __init__(self):
        def __(arg):
            self.bar(arg)
        self.foo = __
    def foo(self, arg):
        print "foo", arg
    def bar(self, arg):
        print "bar", arg

SomeClass().foo("thing") # prints "bar thing"

When creating an instance (after __new__, iirc, but before __init__) Python binds all the methods to automagically supply the instance as the first argument. If you're adding a method later then you need to supply the instance manually. As you are defining the function with self already in scope you don't need to pass it again.

Python's new module is not a solution as it has been deprecated since 2.6. If you want to create a "real" instance method do it with the partial decorator like this:

import functools

class SomeClass(object):
    def __init__(self):
        def __(self, arg):
            self.bar(arg)
        self.foo = functools.partial(__, self)
    def foo(self, arg):
        print "foo", arg
    def bar(self, arg):
        print "bar", arg

SomeClass().foo("thing") # prints "bar thing"


The issue is that you are trying to add a new instance method (not class method) and it is not binding properly. Python has a module function to manually bind functions to instances.

import new
self.method = new.instancemethod(func, self, class)

Edit: Apparently the new module is deprecated. Use the types module instead for metamagic.

import types
self.method = types.MethodType(func, self, class)


sj26's solution is a good one. Another alternative, if you want to set up a method that can be overloaded with any user-supplied function or with another of the object's methods, is build a custom descriptor. This descriptor can be used as a decorator (analogous to @classmethod or @staticmethod); and it allows you to store a function in an instance's dictionary, and returns it as a method:

import types

class overloadable(object):
    def __init__(self, func):
        self._default_func = func
        self._name = func.__name__

    def __get__(self, obj, type=None):
        func = obj.__dict__.get(self._name, self._default_func)
        return types.MethodType(func, obj, type)

    def __set__(self, obj, value):
        if hasattr(value, 'im_func'): value = value.im_func
        obj.__dict__[self._name] = value

    def __delete__(self, obj):
        del obj.__dict__[self._name]

Now we can just decorate a function with "@overloadable":

class SomeClass(object):
    def doStuff(self, string):
        print 'do stuff:', string
    @overloadable
    def overLoaded(self, arg):
        print 'default behavior:', arg

And it'll just do the right thing when we overload it for a given instance:

>>> sc = SomeClass()
>>> sc.overLoaded("test string")    # Before customization
default behavior: test string

>>> sc.overLoaded = sc.doStuff      # Customize
>>> sc.overLoaded("test string")
do stuff: test string

>>> del sc.overLoaded               # Revert to default behavior
>>> sc.overLoaded("test string")
default behavior: test string
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜