Override a method at instance level
Asked Answered
V

12

127

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!!
Vogel answered 27/12, 2008 at 7:12 Comment(1)
boby.bark = YOUR_NEW_FUNCTION.__get__(boby) will do it. Make sure that your new function accepts a self argument.Sepulcher
M
17

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!!
Marriage answered 27/12, 2008 at 13:44 Comment(11)
@arivero: I thought the "Please do not do this as shown" made that perfectly clear. What other or different words would you like to see to make it more clear that this is not answering the question which was asked, but is providing advice on why it's a bad idea?Marriage
I do not disagree in the advice, nor the OP as it seems. But I assume that people has reasons to ask. Or even if the OP hasn't, other future visitors could. So, IMHO, an answer plus a reprimand is better that just a reprimand.Stalk
@arivero: That didn't answer my question.Marriage
@Marriage I think you should link the "shown" to the actual answer, I don't really have a problem with this being the accepted answer but there are reasons why you need to monkey patch in some circumstances and from my skim reading I took "as shown" to mean what you were showing, rather than a different answer.Bramante
Alright, for the sake of argument: let's say you've got twenty thousand instances of some class that took all night to instantiate. Now you've edited one of the methods of the base class. You want to use the new method, but don't want to wait to instantiate all those objects again. I am not sure in this case that any subclassing would be necessary.Rogozen
Subclassing is a much stronger contract than monkey-patching a method. When possible, strong contracts prevent unexpected behaviour. But in some cases, a looser contract is desirable. In those situations, I would prefer using a callback -- instead of monkey-patching -- because, by allowing some behaviour to be customized, the class contract remains unviolated. Your answer, though practical advice, is quite poor for this question and your coding style is inconsistent.Instar
MethodType from types module solves this problem (see my answer below).Crary
Overriding at the instance level is very useful when you want a particular instance to behave differently. No need to write subclassing etc. This should not be accepted solution. Many other solutions below.Negligee
Never say Never. There is probably always a good reason to do what the questioner asked.Unending
You said, "You cannot debug monkeypatched code." Eclipse and PyDev can debug it just fine.Unending
This does not answer the question at all. This shows how to override an inherited method at CLASS level. The OP is asking for overriding a method at INSTANCE level (when the class has already been initialized). I am currently in the same situation where I want do just that, as overriding inherited methods at class level is not an option.Simon
H
195

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"
Halcyon answered 27/12, 2008 at 7:35 Comment(10)
A small comment, when you do funcType(new_bark, foo, Dog) its adding the name == bark and instance method Dog.new_bark in foo.__dict__ right ? So, when you call again it first lookup into the instance dictionary and call for that,Slavery
Please explain what this does, especially what funcType does and why it is necessary.Dander
I think this can be made a bit simpler and more explicit by using funcType = types.MethodType (after importing types) instead of funcType = type(Dog.bark).Subrogate
I guess this does not work with Python 3. I am getting a "TypeError: function() argument 1 must be code, not function" error. Any suggestions for Python 3?Bamboo
You can also call __get__ on the function to bind it to the instance.Sianna
@Bamboo The solution of Mad Physicist using __get__ also works in Python 3.Subclavian
This is almost what I actually need for my situation, but I would also like to not touch or change anything in the instance that I am overriding. Is that possible in any way?Simon
@Simon yes, you can make the override local: tmp=foo.bark; foo.bark=funcType(new_bark, foo, Dog); foo.bark(); foo.bark=tmpOdin
On Python 3.7.11 I got TypeError: method expected 2 arguments, got 3 on the foo.bark = assignment. Works fine without including the class as a third argument.Estragon
I'd use foo.__class__ rather than Dog as an input for funcType, as you don't always have the class definition in your scopeSepulcher
C
82

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!!
Crary answered 10/2, 2017 at 7:46 Comment(3)
if using python typing, mypy raises the error Cannot assign to a method see related issue. for now, the workaround is to use setattr(boby, "bar", types.MethodType(_bark, boby)Clymer
This should be the accepted answer. It works well in both Python 2&3Tahiti
I tried overriding __get_item__ which didn't work properly: https://mcmap.net/q/175808/-overriding-special-methods-on-an-instanceMeridith
S
54

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.

Sianna answered 15/10, 2017 at 16:12 Comment(13)
This should be the accepted answer: it's clean and works in Python 3.Subclavian
@BlenderBender. I appreciate your supportSianna
What is the difference between this answer and the one above yours from @Harshal Dhumai?Simon
@1313. Functionally, there shouldn't be much difference. I suspect the answer above may accept a wider range of callables as input, but without reading the docs and playing around, I'm not sure.Sianna
Python 2 docs for this: docs.python.org/2/howto/descriptor.html#functions-and-methods . So theoretically it's the same as @Harshal Dhumai's answer.Medial
What would you propose to do if I want to wrap the original bark method and call it in the new method, like one would do with super().bark()?Funky
@Whadupapp. That may merit its own question. I'm not sure exactly what you're asking, so you would have to come up with a complete example.Sianna
@djvg. Please don't deliberately mess up my choice of formatting.Sianna
@MadPhysicist: sorry about that. I wasn't aware it was a deliberate choice of formatting. Thought it was some leftover style from the old days. Only trying to help.Penang
@djvg. I appreciate the spirit in which the edit was made. Unfortunately <pre> tags are the only way to mix code blocks with other formatting. Triple backticks and indentation do not support this.Sianna
You should do foo.bark = new_bark.__get__(foo, foo.__class__) rather than foo.bark = new_bark.__get__(foo, Dog). You don't always have the class name in your scope, e.g., when you get your object from a "factory"-like function, rather than an explicit constructor. So it is more robust.Sepulcher
@SomethingSomething. That's a very good point. In fact you could omit the second argument entirely for even better consistencySianna
Thanks. Your answer seems to be very popular, so I'd consider updating it if I were you, discussing the turns-out-3 alternatives.Sepulcher
S
28
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.

Samsun answered 27/12, 2008 at 7:46 Comment(3)
IMHO using the original signature adds to readability, especially if the function is defined elsewhere in the code, not near the instance. The exception would be the case where the overriding method is also used independently as a function. Of course, in this simple example, it doesn't matter.Halcyon
I don't understand why this isn't the accepted answer. It is called patching and this is the correct way to do it (e.g. boby = Dog() and boby.bark = new_bark). It is incredibly useful in unit testing for controls. For more explanation see tryolabs.com/blog/2013/07/05/run-time-method-patching-python (examples) - no I'm not affiliated with the linked site or author.Welch
new_bark method do not have access to self (instance) so there is no way user can access instance properties in new_bark. Instead one need to use MethodType from types module (see my answer below).Crary
M
17

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!!
Marriage answered 27/12, 2008 at 13:44 Comment(11)
@arivero: I thought the "Please do not do this as shown" made that perfectly clear. What other or different words would you like to see to make it more clear that this is not answering the question which was asked, but is providing advice on why it's a bad idea?Marriage
I do not disagree in the advice, nor the OP as it seems. But I assume that people has reasons to ask. Or even if the OP hasn't, other future visitors could. So, IMHO, an answer plus a reprimand is better that just a reprimand.Stalk
@arivero: That didn't answer my question.Marriage
@Marriage I think you should link the "shown" to the actual answer, I don't really have a problem with this being the accepted answer but there are reasons why you need to monkey patch in some circumstances and from my skim reading I took "as shown" to mean what you were showing, rather than a different answer.Bramante
Alright, for the sake of argument: let's say you've got twenty thousand instances of some class that took all night to instantiate. Now you've edited one of the methods of the base class. You want to use the new method, but don't want to wait to instantiate all those objects again. I am not sure in this case that any subclassing would be necessary.Rogozen
Subclassing is a much stronger contract than monkey-patching a method. When possible, strong contracts prevent unexpected behaviour. But in some cases, a looser contract is desirable. In those situations, I would prefer using a callback -- instead of monkey-patching -- because, by allowing some behaviour to be customized, the class contract remains unviolated. Your answer, though practical advice, is quite poor for this question and your coding style is inconsistent.Instar
MethodType from types module solves this problem (see my answer below).Crary
Overriding at the instance level is very useful when you want a particular instance to behave differently. No need to write subclassing etc. This should not be accepted solution. Many other solutions below.Negligee
Never say Never. There is probably always a good reason to do what the questioner asked.Unending
You said, "You cannot debug monkeypatched code." Eclipse and PyDev can debug it just fine.Unending
This does not answer the question at all. This shows how to override an inherited method at CLASS level. The OP is asking for overriding a method at INSTANCE level (when the class has already been initialized). I am currently in the same situation where I want do just that, as overriding inherited methods at class level is not an option.Simon
B
13

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!!
Byrn answered 13/2, 2019 at 4:47 Comment(0)
P
0

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
Persson answered 27/12, 2008 at 12:6 Comment(0)
T
0

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.

Trichina answered 9/3, 2021 at 21:48 Comment(0)
T
0

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
Totemism answered 5/9, 2023 at 14:38 Comment(0)
P
-4

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
Persson answered 27/12, 2008 at 14:10 Comment(1)
If it needs to be "managed", then -- to me -- something's wrong. Especially when there's a first-class language feature that already does the job.Marriage
K
-4

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

Kikelia answered 7/6, 2013 at 6:30 Comment(0)
E
-4

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

https://mcmap.net/q/175809/-override-module-method-where-from-import-is-used

import a

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

a.print_message = _new_print_message

import b
b.execute()
Estoppel answered 1/3, 2017 at 10:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.