开发者

Is it possible to change an instance's method implementation without changing all other instances of the same class? [duplicate]

This question already has answers here: Override a method at instance level (11 answers) Closed 2 years ago.

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 = showyIm开发者_StackOverflow中文版p

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? :)


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__().


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


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!


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()
>>> 


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜