Override a method at instance level
You need to use MethodType from the types
module. The purpose of MethodType
is to overwrite instance level methods (so that self
can be available in overwritten methods).
See the below example.
import types
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print "WoOoOoF!!"
boby.bark = types.MethodType(_bark, boby)
boby.bark() # WoOoOoF!!
Yes, it's possible:
class Dog:
def bark(self):
print "Woof"
def new_bark(self):
print "Woof Woof"
foo = Dog()
funcType = type(Dog.bark)
# "Woof"
foo.bark()
# replace bark with new_bark for this object only
foo.bark = funcType(new_bark, foo, Dog)
foo.bark()
# "Woof Woof"
To explain @codelogic's excellent answer, I propose a more explicit approach. This is the same technique that the .
operator goes thorough to bind a class method when you access it as an instance attribute, except that your method will actually be a function defined outside of a class.
Working with @codelogic's code, the only difference is in how the method is bound. I am using the fact that functions and methods are non-data descriptors in Python, and invoking the __get__
method. Note particularly that both the original and the replacement have identical signatures, meaning that you can write the replacement as a full class method, accessing all the instance attributes via self
.
class Dog: def bark(self): print "Woof" def new_bark(self): print "Woof Woof" foo = Dog() # "Woof" foo.bark() # replace bark with new_bark for this object only foo.bark = new_bark.__get__(foo, Dog) foo.bark() # "Woof Woof"
By assigning the bound method to an instance attribute, you have created a nearly complete simulation of overriding a method. One handy feature that is missing is access to the no-arg version of super
, since you are not in a class definition. Another thing is that the __name__
attribute of your bound method will not take the name of the function it is overriding, as it would in class definition, but you can still set it manually. The third difference is that your manually-bound method is a plain attribute reference that just happens to be a function. The .
operator does nothing but fetch that reference. When invoking a regular method from an instance on the other hand, the binding process creates a new bound method every time.
The only reason that this works, by the way, is that instance attributes override non-data descriptors. Data descriptors have __set__
methods, which methods (fortunately for you) do not. Data descriptors in the class actually take priority over any instance attributes. That is why you can assign to a property: their __set__
method gets invoked when you try to make an assignment. I personally like to take this a step further and hide the actual value of the underlying attribute in the instance's __dict__
, where it is inaccessible by normal means exactly because the property shadows it.
You should also keep in mind that this is pointless for magic (double underscore) methods. Magic methods can of course be overridden in this way, but the operations that use them only look at the type. For example, you can set __contains__
to something special in your instance, but calling x in instance
would disregard that and use type(instance).__contains__(instance, x)
instead. This applies to all magic methods specified in the Python data model.