replace mothod for only one object but not for a class

  • Thread starter Bruno Desthuilliers
  • Start date
B

Bruno Desthuilliers

George Sakkis a écrit :
Please post the actual code that doesn't work. The following works as
expected:

... def foo(self): return 'Original'
...
'Modified'

Except that b.foo is a plain function, not a method:
.... def foo(self): return "original called on %s" % self
....Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Traceback (most recent call last):
 
B

Bruno Desthuilliers

hofer a écrit :
Hi,

I have multiple objects all belonging to the same class
(which I didn't implement and whose code I don't want to modify)

Now I'd like to change one method for one object only (after it has
been created) without adding any overhead
to the call of the other object's methods.


Is this possible?

Yes.

If the class is a new-style one [1], it just requires invoking the
descriptor protocol by yourself to get a bound method, ie:
.... def foo(self): return "original %s" % self
....
If it's a classic class, you can get by using either new.instancemethod
or types.MethodType:
.... def bar(self): return "bar %s" % self
....
Note that using either new.instancemethod or types.MethodType will also
work with new-style classes - it just requires an additional import !-)


[1] for a new style class, type(cls) will return type. For a "classic"
class, type(cls) will return classobj.


HTH

(snip)
P.S. I guess, that there is a computer science term for what I try to
achieve.
If anybody knew it I would be interested to learn it as well.

I don't know of any "official" cs term for this. In prototype-based
OOPL's, setting attributes on a per-object basis is just ordinary
programming. Python is class-based, but on some (if not a lot) of
aspects it's quite close to prototype-based languages.

An unofficial cs term is "monkeypatching". It's usually understood as a
per-class or per-module replacement, but is still appropriate for a
per-object replacement (or addition FWIW).
 
H

hofer

Hi,

I have multiple objects all belonging to the same class
(which I didn't implement and whose code I don't want to modify)

Now I'd like to change one method for one object only (after it has
been created) without adding any overhead
to the call of the other object's methods.


Is this possible?

Example
##### This is NOT what I'd like to do
##### as it overwrites the method for all objects of this class
o1 = aclass()
o2 = aclass()
# call original method
o1.method()
o2.method()
# overwrite the method for the entire class
aclass.method = mymethod
o1.method() # now new method
o2.method() # now new method


####### What doesn't work, but what I'd like to do
o1 = aclass()
o2 = aclass()
# call original method
o1.method()
o2.method()
# overwrite the method for the entire class
o1.method = mymethod
o1.method() # now new method
o2.method() # still old method


thanks for any pointers.


P.S. I guess, that there is a computer science term for what I try to
achieve.
If anybody knew it I would be interested to learn it as well.
 
M

Miki

from functools import partial

class Point:
def __init__(self, x, y):
self.x, self.y = x, y

def show(self, n):
for i in range(n):
print "Point: (%s, %s)" % (self.x, self.y)

def new_method(obj, func):
def method(*args, **kw):
return func(obj, *args, **kw)

return method


p1 = Point(1, 2)
p2 = Point(3, 4)

def show(self, n):
print "Not a Point: %s-%s" % (self.x, self.y)
p2.show = partial(show, p2)

p1.show(3)
p2.show(3)

HTH,
 
G

George Sakkis

Hi,

I have multiple objects all belonging to the same class
(which I didn't implement and whose code I don't want to modify)

Now I'd like to change one method for one object only (after it has
been created) without adding any overhead
to the call of the other object's methods.

Is this possible?

Example
##### This is NOT what I'd like to do
##### as it overwrites the method for all objects of this class
o1 = aclass()
o2 = aclass()
# call original method
o1.method()
o2.method()
# overwrite the method for the entire class
aclass.method = mymethod
o1.method() # now new method
o2.method() # now new method

####### What doesn't work, but what I'd like to do
o1 = aclass()
o2 = aclass()
# call original method
o1.method()
o2.method()
# overwrite the method for the entire class
o1.method = mymethod
o1.method() # now new method
o2.method() # still old method

thanks for any pointers.

Please post the actual code that doesn't work. The following works as
expected:
... def foo(self): return 'Original'
... 'Modified'


HTH,
George
 
J

Jason Scheirer

Please post the actual code that doesn't work. The following works as
expected:

    >>> class A(object):
    ...     def foo(self): return 'Original'
    ...
    >>> a = A()
    >>> b = A()
    >>> a.foo()
    'Original'
    >>> b.foo()
    'Original'
    >>> b.foo = lambda: 'Modified'
    >>> a.foo()
    'Original'
    >>> b.foo()
    'Modified'

HTH,
George

What you're doing is called monkeypatching. I consider it dangerous,
but to each his own. Python's metaprogramming facilities can handle
it.

The lambda approach can leak, I've had garbage collection issues when
shuffling around bound methods (my objects weren't being collected
when expected, basically). The partial approach is cool, too, but it
won't reliably work with things like __getattr__ and __setattr__,
which I also learned the hard way. The probable best way of going
about it is to define a new class that implements your one method, and
use some on-the-fly magic to create a NEW class that inherits from
both your monkeypatch class and your current instance's class:

In [2]: class main_implementation(object):
...: def a(self):
...: print 'a'
...: def b(self, argb='b'):
...: print 'B says %r' % argb
...:
...:

In [6]: class new_implementation_of_a(object):
...: def a(self):
...: print "This is a new implementation of A"
...:
...:

In [7]: mymain = main_implementation()

In [8]: mymain.a()
a

In [9]: mymain.__class__ = type(mymain.__class__.__name__+'MODIFIED',
(new_implementation_of_a, mymain.__class__), {})

In [10]: mymain
Out[10]: <__main__.main_implementationMODIFIED object at 0x0137EA50>

In [11]: mymain.a()
This is a new implementation of A

In [12]: mymain.b()
B says 'b'

The magic here occurs when you create a new class on-the-fly using the
type() built-in function and assign it to the instance's __class__
attribute.

Good luck.
 
B

Bruno Desthuilliers

Christian Heimes a écrit :
Bruno said:
If the class is a new-style one [1], it just requires invoking the
descriptor protocol by yourself to get a bound method, ie:

Another note about new style classes:
You can NOT overwrite most magic methods (__*__) on the instance. Most
magic methods are only looked up on the class object.
Yes, indeed, I forgot to mention that. Thanks for the precious precision.
 
G

George Sakkis

George Sakkis a écrit :





Except that b.foo is a plain function, not a method:

... def foo(self): return "original called on %s" % self
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>

Traceback (most recent call last):

You're right of course; that's what you get with minimal testing ;)
Still it works with a small modification, binding self to b as default
argument:

b.foo = lambda self=b: "modified called on %s" % self

The functools.partial() solution shown before is the generalization of
this for more than one arguments. So I'm wondering, except for the
callable's exact type (bound method vs function vs functools.partial
object) is there any other difference and a reason to prefer one over
the other ? If not, functools.partial() seems more readable than
calling explicitly the descriptor.

George
 
B

Bruno Desthuilliers

George Sakkis a écrit :
(snip)
You're right of course; that's what you get with minimal testing ;)
Still it works with a small modification, binding self to b as default
argument:

b.foo = lambda self=b: "modified called on %s" % self

Ok, now with a real use case : use a named function instead of a lambda,
and define the function before having access to b !-)
The functools.partial() solution shown before is the generalization of
this for more than one arguments. So I'm wondering, except for the
callable's exact type (bound method vs function vs functools.partial
object) is there any other difference and a reason to prefer one over
the other ?

Consistency ? Possibly performances (NB : not benchmarked - might be
worth a little homework to check this out) ?


If not, functools.partial() seems more readable than
calling explicitly the descriptor.

Perhaps is it just me starting to getting senile and resistant to new
ideas, but while it does indeed work, I see the partial() solution as a
WTF. Invoking function.__get__ *is* the obvious[1] way to get a bound
method from a function.

[1] for anyone that know how Python turns functions into methods, that is...

But indeed, methods are specialized partial applications, and your
solution is perfectly valid.
 
B

Bruno Desthuilliers

Bruno Desthuilliers a écrit :
hofer a écrit :
Hi,

I have multiple objects all belonging to the same class
(which I didn't implement and whose code I don't want to modify)

Now I'd like to change one method for one object only (after it has
been created) without adding any overhead
to the call of the other object's methods.


Is this possible?

Yes.

If the class is a new-style one [1], it just requires invoking the
descriptor protocol by yourself to get a bound method, ie:
(snip)


If it's a classic class, you can get by using either new.instancemethod
or types.MethodType:

Either I'm starting to get old or I need some vacations... Manually
invoking function.__get__(obj, type(obj)) also work with classic
classes, of course.

(snip)
 
H

hofer

Hi,

I have multiple objects all belonging to the same class
 (which I didn't implement and whose code I don't want to modify)

Now I'd like to change one method for one object only (after it has
been created) without adding any overhead
to the call of the other object's methods.

Is this possible?

Example
##### This is NOT what I'd like to do
##### as it overwrites the method for all objects of this class
o1 = aclass()
o2 = aclass()
# call original method
o1.method()
o2.method()
# overwrite the method for the entire class
aclass.method = mymethod
o1.method() # now new method
o2.method() # now new method

####### What doesn't work, but what I'd like to do
o1 = aclass()
o2 = aclass()
# call original method
o1.method()
o2.method()
# overwrite the method for the entire class
o1.method = mymethod
o1.method() # now new method
o2.method() # still old method

thanks for any pointers.

P.S. I guess, that there is a computer science term for what I try to
achieve.
If anybody knew it I would be interested to learn it as well.


Thanks a lot this works.
Though I must admint, that even after reading the doc I don't really
understand why.
 
H

hofer

Hi

Thanks for all of your answers:

Here an example with three of the suggested solutions:
(I didn't succeed in implementing Jason's solution with my
example)

########################################################
import threading
# some objects
a = threading.Event()
b = threading.Event()
c = threading.Event()
d = threading.Event()

def run_dly(o): # a test function
print o,"start",
o.wait(1)
print "stop"

# unmodified test
run_dly(a)
run_dly(b)
run_dly(c)
run_dly(d)

# The new Method
def verbose_wait(self,dly):
print "VERBOSE",
threading._Event.wait(self,dly)

### Implemented with partial
from functools import partial
b.wait = partial(verbose_wait,b)

### with __get__ for new classes
c.wait = verbose_wait.__get__(c,type(c))

## with new for old classes
import new
d.wait = new.instancemethod(verbose_wait,d,type(d))

run_dly(a)
run_dly(b)
run_dly(c)
run_dly(d)
############# end
thanks again

Hofer
 
B

Bruno Desthuilliers

hofer a écrit :
Hi


Thanks for all of your answers:

Here an example with three of the suggested solutions:
(I didn't succeed in implementing Jason's solution with my
example)

########################################################
import threading
# some objects
a = threading.Event()
b = threading.Event()
c = threading.Event()
d = threading.Event()

def run_dly(o): # a test function
print o,"start",
o.wait(1)
print "stop"

# unmodified test
run_dly(a)
run_dly(b)
run_dly(c)
run_dly(d)

# The new Method
def verbose_wait(self,dly):
print "VERBOSE",
threading._Event.wait(self,dly)

Note that given your use case, you could have used a decorator here
instead...

def verbose(method):
def _verbose(*args, **kw):
print "%s called on %s" % (method.__name__, method.im_self)
print "args : ", args, " - kwargs : ", kw
return method(*args, **kw)

_verbose.__name__ = "verbose wrapper for %s" % method.__name__
return _verbose

b.wait = verbose(b.wait)



Or if you want a more extensible - and "reversible" - solution:


class VerboseMethod(object):
def __init__(self, method, before=None, after=None):
# we only want bound methods here
obj = getattr(method, "im_self", None)
if obj is None:
err = "%s expected a bound method, got %s" % (
type(self), method
)
raise ValueError(err)

self._method = method
self._before = before
self._after = after

def _verbose_before(self, *args, **kw):
"""
You subclass VerboseMethod and taylor this to your own needs,
or alternatively pass a 'before' callback to VerboseMethod
that will get called with method, *args, **kw
"""
if callable(self._before):
self._before(self._method, *args, **kw)
return
# default
m = self._method
print "%s about to be called on %s" % (m.__name__, m.im_self)
print "args : ", args, " - kwargs : ", kw

def _verbose_after(self, result, *args, **kw):
"""
You subclass VerboseMethod and taylor this to your own needs,
or alternatively pass an 'after' callback to VerboseMethod
that will get called with method, result, *args, **kw
"""
if callable(self._after):
self._after(self._method, result, *args, **kw)
return
# default
m = self._method
print "%s called on %s" % (m.__name__, m.im_self)
print "args : ", args, " - kwargs : ", kw
print "result : ", result

def __call__(self, *args, **kw):
self._verbose_before(*args, **kw)
result = self._method(*args, **kw)
self._verbose_after(result, *args, **kw)
return result

def drop(self):
"""restore the original method..."""
obj = self._method.im_self
delattr(obj, self._method.__name__)



class B(object):
def __init__(self, name):
self.name = name

def wait(self, dly=42):
return "%s.wait(%s)" % (self.name, dly)

b1 = B('b1')
b2 = B('b2')

print b1.wait()
print b2.wait()

b1.wait = VerboseMethod(b1.wait)
print b1.wait()
print b2.wait()

b1.wait.drop()
print b1.wait()
print b2.wait()

def before(m, *args, **kw):
print "test before"
print m, args, kw


def after(m, r, *args, **kw):
print "test after"
print m, r, args, kw

b1.wait = VerboseMethod(b1.wait, before=before, after=after)
print b1.wait()
print b2.wait()

b1.wait.drop()
print b1.wait()
print b2.wait()


HTH
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top