pistacchio
pistacchio

Reputation: 58883

Override a method at instance level

Is there a way in Python to override a class method at instance level? For example:

class Dog:
    def bark(self):
        print "WOOF"

boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!

Upvotes: 135

Views: 100496

Answers (13)

kviLL
kviLL

Reputation: 313

Python 3 Solution

The other answers still use Python 2. Here's an updated solution:

class Dog:
    def bark(self):
        print("WOOF")

my_dog = Dog()

# "WOOF"
my_dog.bark()

def new_bark(self):
    print("WoOoOoF!!")

# Replace bark() with new_bark() for my_dog only
my_dog.bark = new_bark.__get__(my_dog, Dog)

# "WoOoOoF!!"
my_dog.bark()

Note that this doesn't work for magic methods. For that you need a wrapper class.

Upvotes: 0

Danil
Danil

Reputation: 5181

If you try to "wrap" the base method you will get {RecursionError}maximum recursion depth exceeded in comparison error.

To resolve this use self.__class__.method instead of self.method


In [21]: import types
    ...: 
    ...: 
    ...: class Dog:
    ...:     def bark(self):
    ...:         print("WOOF")
    ...: 
    ...: 
    ...: def my_bark(self):
    ...:     print("RRRR")
    ...:     self.__class__.bark(self)
    ...: 
    ...: 
    ...: dog = Dog()
    ...: dog.bark = types.MethodType(my_bark, dog)
    ...: dog.bark()
    ...: 
RRRR
WOOF

Upvotes: 0

Mad Physicist
Mad Physicist

Reputation: 114320

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.

Upvotes: 55

ntjess
ntjess

Reputation: 1460

Be careful when you need to call the old method inside the new method:

import types

class Dog:
  def bark(self):
    print("WOOF")

boby = Dog()
boby.bark() # WOOF

def _bark(self):
  self.bark()
  print("WoOoOoF!!")

boby.bark = types.MethodType(_bark, boby)

boby.bark() # Process finished with exit code -1073741571 (0xC00000FD) [stack overflow]
# This also happens with the  '__get__' solution

For these situations, you could use the following:

def _bark(self):
  Dog.bark(self)
  print( "WoOoOoF!!") # Calls without error

But what if someone else in the library already overrode foo's bark method? Then Dog.bark(foo) is not the same as foo.bark! In my experience, the easiest solution that works in both of these cases is

# Save the previous definition before overriding
old_bark = foo.bark
def _bark(self):
  old_bark()
  print("WoOoOoF!!")
foo.bark = _bark
# Works for instance-overridden methods, too

Most of the time, subclassing and using super is the correct way to handle this situation. However, there are times where such monkeypatching is necessary and will fail with a stack overflow error unless you are a bit more careful.

Upvotes: 0

Harshal Dhumal
Harshal Dhumal

Reputation: 1429

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

Upvotes: 88

user1285245
user1285245

Reputation: 433

Since no one is mentioning functools.partial here:

from functools import partial

class Dog:
    name = "aaa"
    def bark(self):
        print("WOOF")

boby = Dog()
boby.bark() # WOOF

def _bark(self):
    print("WoOoOoF!!")

boby.bark = partial(_bark, boby)
boby.bark() # WoOoOoF!!

Upvotes: 13

Ruvalcaba
Ruvalcaba

Reputation: 525

I found this to be the most accurate answer to the original question

https://stackoverflow.com/a/10829381/7640677

import a

def _new_print_message(message):
    print "NEW:", message

a.print_message = _new_print_message

import b
b.execute()

Upvotes: -4

codelogic
codelogic

Reputation: 73622

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"

Upvotes: 197

nagimsit
nagimsit

Reputation: 3

Dear this is not overriding you are just calling the same function twice with the object. Basically overriding is related to more than one class. when same signature method exist in different classes then which function your are calling this decide the object who calls this. Overriding is possible in python when you make more than one classes are writes the same functions and one thing more to share that overloading is not allowed in python

Upvotes: -4

JV.
JV.

Reputation: 2698

Though I liked the inheritance idea from S. Lott and agree with the 'type(a)' thing, but since functions too have accessible attributes, I think the it can be managed this way:

class Dog:
    def __init__(self, barkmethod=None):
        self.bark=self.barkp
        if barkmethod:
           self.bark=barkmethod
    def barkp(self):
        """original bark"""
        print "woof"

d=Dog()
print "calling original bark"
d.bark()
print "that was %s\n" % d.bark.__doc__

def barknew():
    """a new type of bark"""
    print "wooOOOoof"

d1=Dog(barknew)
print "calling the new bark"
d1.bark()
print "that was %s\n" % d1.bark.__doc__

def barknew1():
    """another type of new bark"""
    print "nowoof"

d1.bark=barknew1
print "another new"
d1.bark()
print "that was %s\n" % d1.bark.__doc__

and the output is :

calling original bark
woof
that was original bark

calling the new bark
wooOOOoof
that was a new type of bark

another new
nowoof
that was another type of new bark

Upvotes: -4

S.Lott
S.Lott

Reputation: 391852

Please do not do this as shown. You code becomes unreadable when you monkeypatch an instance to be different from the class.

You cannot debug monkeypatched code.

When you find a bug in boby and print type(boby), you'll see that (a) it's a Dog, but (b) for some obscure reason it doesn't bark correctly. This is a nightmare. Do not do it.

Please do this instead.

class Dog:
    def bark(self):
        print "WOOF"

class BobyDog( Dog ):
    def bark( self ):
        print "WoOoOoF!!"

otherDog= Dog()
otherDog.bark() # WOOF

boby = BobyDog()
boby.bark() # WoOoOoF!!

Upvotes: 18

JV.
JV.

Reputation: 2698

Since functions are first class objects in Python you can pass them while initializing your class object or override it anytime for a given class instance:

class Dog:
    def __init__(self,  barkmethod=None):
        self.bark=self.barkp
        if barkmethod:
           self.bark=barkmethod
    def barkp(self):
        print "woof"

d=Dog()
print "calling original bark"
d.bark()

def barknew():
    print "wooOOOoof"

d1=Dog(barknew)
print "calling the new bark"
d1.bark()

def barknew1():
    print "nowoof"

d1.bark=barknew1
print "calling another new"
d1.bark()

and the results are

calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof

Upvotes: 0

nosklo
nosklo

Reputation: 222862

class Dog:
    def bark(self):
        print "WOOF"

boby = Dog()
boby.bark() # WOOF

# METHOD OVERRIDE
def new_bark():
    print "WoOoOoF!!"
boby.bark = new_bark

boby.bark() # WoOoOoF!!

You can use the boby variable inside the function if you need. Since you are overriding the method just for this one instance object, this way is simpler and has exactly the same effect as using self.

Upvotes: 28

Related Questions