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
精彩评论