34

I do not know python very much (never used it before :D), but I can't seem to find anything online. Maybe I just didn't google the right question, but here I go:

I want to change an instance's implementation of a specific method. When I googled for it, I found you could do it, but it changes the implementation for all other instances of the same class, for example:

def showyImp(self):
    print self.y

class Foo:
    def __init__(self):
        self.x = "x = 25"
        self.y = "y = 4"

    def showx(self):
        print self.x

    def showy(self):
         print "y = woohoo"

class Bar:
    def __init__(self):
        Foo.showy = showyImp
        self.foo = Foo()

    def show(self):
        self.foo.showx()
        self.foo.showy()

if __name__ == '__main__':
    b = Bar()
    b.show()
    f = Foo()
    f.showx()
    f.showy()

This does not work as expected, because the output is the following:

x = 25

y = 4

x = 25

y = 4

And I want it to be:

x = 25

y = 4

x = 25

y = woohoo

I tried to change Bar's init method with this:

def __init__(self):
    self.foo = Foo()
    self.foo.showy = showyImp

But I get the following error message:

showyImp() takes exactly 1 argument (0 given)

So yeah... I tried using setattr(), but seems like it's the same as self.foo.showy = showyImp.

Any clue? :)

Community
  • 1
  • 1
  • 2
    Is there a particular reason you're not doing this with inheritance? – Jack M. Oct 30 '09 at 01:50
  • 2
    Yes, because I have very limited access to the created object. I'm not the one creating it (hence the non-possibility of inheritance), and I cannot modify the original object's code (also because it would not make sense anyway, I need to change the method only in a context that is completely different from what the object is initaly intended for :). – Thibault Martin-Lagardette Oct 30 '09 at 02:56

6 Answers6

60

Since Python 2.6, you should use the types module's MethodType class:

from types import MethodType

class A(object):
    def m(self):
        print 'aaa'

a = A()

def new_m(self):
    print 'bbb'

a.m = MethodType(new_m, a)

As another answer pointed out, however, this will not work for 'magic' methods of new-style classes, such as __str__().

Josh M
  • 766
  • 7
  • 2
  • Thank you! The accepted answer doesn't work in Python 3, and the documentation page doesn't make it very clear how to use `MethodType`. – coredumperror Jun 23 '16 at 19:54
  • Its not a huge deal, I guess (though if you know how to fix this, then I'd appreciate the help), but... This overwrites a method of an instantiated object, not the class itself... so when a new instance of the class is created, you have to run this code to change the method, as opposed to having the method change in the physical class creator. – Shmack Sep 24 '21 at 05:59
31

This answer is outdated; the answer below works with modern Python

Everything you wanted to know about Python Attributes and Methods.

Yes, this is an indirect answer, but it demonstrates a number of techniques and explains some of the more intricate details and "magic".

For a "more direct" answer, consider python's new module. In particular, look at the instancemethod function which allows "binding" a method to an instance -- in this case, that would allow you to use "self" in the method.

import new
class Z(object):
  pass
z = Z() 
def method(self):
  return self
z.q = new.instancemethod(method, z, None)
z is z.q()  # true
offby1
  • 6,767
  • 30
  • 45
10

If you ever need to do it for a special method (which, for a new-style class -- which is what you should always be using and the only kind in Python 3 -- is looked up on the class, not the instance), you can just make a per-instance class, e.g....:

self.foo = Foo()
meths = {'__str__': lambda self: 'peekaboo!'}
self.foo.__class__ = type('yFoo', (Foo,), meths)

Edit: I've been asked to clarify the advantages of this approach wrt new.instancemethod...:

>>> class X(object): 
...   def __str__(self): return 'baah'
... 
>>> x=X()
>>> y=X()
>>> print x, y
baah baah
>>> x.__str__ = new.instancemethod(lambda self: 'boo!', x)
>>> print x, y
baah baah

As you can see, the new.instancemethod is totally useless in this case. OTOH...:

>>> x.__class__=type('X',(X,),{'__str__':lambda self:'boo!'})
>>> print x, y
boo! baah

...assigning a new class works great for this case and every other. BTW, as I hope is clear, once you've done this to a given instance you can then later add more method and other class attributes to its x.__class__ and intrinsically affect only that one instance!

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
5

If you're binding to the instance, you shouldn't include the self argument:

>>> class Foo(object):
...     pass
... 
>>> def donothing():
...     pass
... 
>>> f = Foo()
>>> f.x = donothing
>>> f.x()
>>> 

You do need the self argument if you're binding to a class though:

>>> def class_donothing(self):
...     pass
... 
>>> foo.y = class_donothing
>>> f.y()
>>> 
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
Jason Baker
  • 192,085
  • 135
  • 376
  • 510
0

Your example is kind of twisted and complex, and I don't quite see what it has to do with your question. Feel free to clarify if you like.

However, it's pretty easy to do what you're looking to do, assuming I'm reading your question right.

class Foo(object):
    def bar(self):
        print('bar')

def baz():
    print('baz')

In an interpreter ...

>>> f = Foo()
>>> f.bar()
bar
>>> f.bar = baz
>>> f.bar()
baz
>>> g = Foo()
>>> g.bar()
bar
>>> f.bar()
baz
hanksims
  • 1,479
  • 2
  • 12
  • 22
  • 1
    The problem with this solution is that it does not use `self` (as a parameter), which I do need, and I can't do f.bar(f), mainly because I don't want to (it looks very anti-pattern like). – Thibault Martin-Lagardette Oct 30 '09 at 02:59
0

Do Not Do This.

Changing one instance's methods is just wrong.

Here are the rules of OO Design.

  1. Avoid Magic.

  2. If you can't use inheritance, use delegation.

That means that every time you think you need something magic, you should have been writing a "wrapper" or Facade around the object to add the features you want.

Just write a wrapper.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • @naixn: It's your question. You own the question. You can update the question. If you update the question to contain ALL the facts, you do not have to resort to out-of-band messages. Please update the question with the elaborate justification for a fundamentally flawed design. Your explanation amounts to "I don't want to wrap and delegate." (1) Please UPDATE your question, (2) Please reconsider your flawed design. – S.Lott Oct 31 '09 at 12:49
  • 2
    What if you need a third-party library to behave very slightly differently? In my particular case, I need one specific instance of `Logger` to append a bit of data to each log message that the library sends to the logger's `info()` method. Would it be possible to use wrap-and-delegate for that? – coredumperror Jun 23 '16 at 19:59