Mailing List Archive

Chaning instance methods
I don't understand how to change instance methods. For example:

class Foo:
def __init__(self):
self.data = 42
def m(self):
print "Foo.m"
print dir(self)

def m2(self):
print "m2"
print dir(self)

f = Foo()
f.m()
# this fails
# f.m = m2
# f.m()
Foo.m = m2 # Changes all instances of Foo.m
f.m()

f2 = Foo()
f2.m()

What am I forgetting?

--
Jody Winston
Manager SeisRes
Lamont-Doherty Earth Observatory
RT 9W, Palisades, NY 10964
jody@ldeo.columbia.edu, 914 365 8526, Fax 914 359 1631

Under US Code Title 47, Sec.227(b)(1)(C), Sec.227(a)(2)(B) This email
address may not be added to any commercial mail list with out my
permission. Violation of my privacy with advertising or SPAM will
result in a suit for a MINIMUM of $500 damages/incident, $1500 for
repeats.
Chaning instance methods [ In reply to ]
Is this what you are looking for?

---------

# testchangemethod.py:

class Test:
def m(self):
pass
def m1(self):
print 'I am Test.m1'
def m2(self):
print 'I am Test.m2'

---------

>>> from testchangemethod import Test
>>>
>>> t = Test()
>>> Test.m = Test.m1
>>> t.m()
I am Test.m1
>>> Test.m = Test.m2
>>> t.m()
I am Test.m2

--------

I don't understand this too well myself. However, note that if you
evaluate 'type(Test.m1)' you get something different from what you
get if your evaluate 'type(f)' where f is a function (not a method).

- Dave


Jody Winston <jody@ldgo.columbia.edu> wrote:
> I don't understand how to change instance methods. For example:

> class Foo:
> def __init__(self):
> self.data = 42
> def m(self):
> print "Foo.m"
> print dir(self)

> def m2(self):
> print "m2"
> print dir(self)

> f = Foo()
> f.m()
> # this fails
> # f.m = m2
> # f.m()
> Foo.m = m2 # Changes all instances of Foo.m
> f.m()

> f2 = Foo()
> f2.m()

> What am I forgetting?

> --
> Jody Winston
> Manager SeisRes
> Lamont-Doherty Earth Observatory
> RT 9W, Palisades, NY 10964
> jody@ldeo.columbia.edu, 914 365 8526, Fax 914 359 1631

> Under US Code Title 47, Sec.227(b)(1)(C), Sec.227(a)(2)(B) This email
> address may not be added to any commercial mail list with out my
> permission. Violation of my privacy with advertising or SPAM will
> result in a suit for a MINIMUM of $500 damages/incident, $1500 for
> repeats.
Chaning instance methods [ In reply to ]
Hope this helps.

>>> class X:
... def y(self):
... print 'y1'
...
>>> x=X()
>>> x.y()
y1
>>> def y2(self=x):
... print 'y2'
...
>>> setattr(x,'y',y2)
>>> x.y()
y2
>>>
Chaning instance methods [ In reply to ]
[Jody Winston]
> I don't understand how to change instance methods.

That's good, because Python doesn't have such a thing <wink>. Direct
attributes of instances are never methods without extreme trickery. A
function becomes a method by virtue of being found in an instance's
*class's* dict. Python was designed to allow overriding methods at the
class level, but not at the instance level.

> For example:
>
> class Foo:
> def __init__(self):
> self.data = 42
> def m(self):
> print "Foo.m"
> print dir(self)
>
> def m2(self):
> print "m2"
> print dir(self)
>
> f = Foo()
> f.m()
> # this fails
> # f.m = m2
> # f.m()

Right, a direct attribute of an instance is never a method. Except that
this "works":

import new
f.m = new.instancemethod(m2, f, Foo)
f.m()

This sets f.m to a *bound* instance method that refers to f, which Python
treats as an ordinary function when later referenced via f.m. Without using
the "new" module, you can get the same effect via e.g.

old_Foo_m = Foo.m
Foo.m = m2
f.m = f.m # heh heh <wink>
Foo.m = old_Foo_m

In either case, though, the value of f.m now contains a hidden reference to
f, so you've created a piece of immortal cyclic trash.

subclassing-is-a-lot-easier-ly y'rs - tim
Chaning instance methods [ In reply to ]
On 6 Apr 99, Ce'Nedra took her magical amulet and heard Jody Winston say:

>I don't understand how to change instance methods. For example:
>
>class Foo:
> def __init__(self):
> self.data = 42
> def m(self):
> print "Foo.m"
> print dir(self)
>
>def m2(self):
> print "m2"
> print dir(self)
>
>f = Foo()
>f.m()
># this fails
># f.m = m2
># f.m()

I'm not sure, but I think it works like this... Foo.m and f.m are somehow
related to Foo or its instances... they're methods; m2, however, is just a
function. When you do something like 'f.m = m2' you attach a plain old
function to a class instance. Python apparently does not like this; 'f.m()'
won't work anymore, since the language does not do an automatic conversion
from function to method (although they look the same!).

To call such a function, you could do

f.m = m2
f.m(f) # need to provide 'self' explicitly

which is probably not what you want.

To be honest, I don't really have a good solution to this. Anybody else?

>Foo.m = m2 # Changes all instances of Foo.m
>f.m()

This works because m2 is "injected" though Foo, and thus accepted as a
method. Try to print Foo.m; it'll say "unbound method". And printing f.m will
show that m2 is accepted as a method in f, too. :^S

>f2 = Foo()
>f2.m()

Veel liefs,

+ Hans Nowak (Zephyr Falcon)
+ Homepage (under construction): http://www.cuci.nl/~hnowak/
+ You call me a masterless man. You are wrong. I am my own master.
+ May an orc cheat on your fiance with your life!
Chaning instance methods [ In reply to ]
Tim Peters wrote:

<snip/>

> Right, a direct attribute of an instance is never a method. Except that
> this "works":
>
> import new
> f.m = new.instancemethod(m2, f, Foo)
> f.m()
>
> This sets f.m to a *bound* instance method that refers to f, which Python
> treats as an ordinary function when later referenced via f.m. Without using
> the "new" module, you can get the same effect via e.g.
>
> old_Foo_m = Foo.m
> Foo.m = m2
> f.m = f.m # heh heh <wink>
> Foo.m = old_Foo_m

As a related topic, there is also DOnBeaudry's functor module from
March '97 which makes use of the newmodule internally and is able
to create quite efficient wrappers around callables with bound
parameters. The calling overhead is about twice as large
as for the original function.

This version needed some tweaking to make it run under Python 1.5 .
I just uploaded a slightly modified version to incoming and
hope it will make it into contrib/system.

cheers - chris

--
Christian Tismer :^) <mailto:tismer@appliedbiometrics.com>
Applied Biometrics GmbH : Have a break! Take a ride on Python's
Kaiserin-Augusta-Allee 101 : *Starship* http://starship.python.net
10553 Berlin : PGP key -> http://wwwkeys.pgp.net
PGP Fingerprint E182 71C7 1A9D 66E9 9D15 D3CC D4D7 93E2 1FAE F6DF
we're tired of banana software - shipped green, ripens at home
Chaning instance methods [ In reply to ]
As Tim says "so you've created a piece of immortal cyclic trash"
I love the way that sounds.
It sure would be nice to be able to walk a list of allocated memory and
clean it up if you want to. Or discover what objects are eating the memory
you have.


> Hope this helps.
>
> >>> class X:
> ... def y(self):
> ... print 'y1'
> ...
> >>> x=X()
> >>> x.y()
> y1
> >>> def y2(self=x):
> ... print 'y2'
> ...
> >>> setattr(x,'y',y2)
> >>> x.y()
> y2
> >>>
>
>
Chaning instance methods [ In reply to ]
Jody Winston wrote:
>
> I don't understand how to change instance methods. For example:
> ...
> What am I forgetting?

On a first thought, you seem to forget Python's internals (that you're
supposed to forget, BTW) and people helped you generously on this by
introducing you, and me, to trickery.

On a second thought, I'll follow their example ;-) but I'll try to do it
the easy (OO) way; it's easy enough not to be described so far, but it
seems more logical to me, so here goes.

If Foo is a class and f is an instance of Foo, by changing the instance
method f.m (for whatever insane reason), one augments f's behavior beyond
the limits prescribed by Foo. By doing so, one withdraws f from the set of
"similar" objects, instances of Foo, that share a common set of properties
(operations), defined by the Foo class. (right, this is Object theory).

Indeed, f becomes some particular object which is no more part of the Foo
gang. Pushing that further, we have reasons to believe that after the
change, we're in a situation where f belongs to some Bar gang, where
Foo and Bar differ only in the 'm' method, just like if f were an instance
of Bar from the start.

In fact, it is Bar that has the desired augmented interface, not f.
Bar is the incarnation of the desired incremental code evolution <ahem>
done the OO way through class inheritance (not with instance hacking).

Thus we arrive at the easy & boring solution, which goes along these lines:
(it's an instance hack too, but a recognized one in metaobject communities)

>>> class Foo:
... def m(self):
... print "Foo.m"
...
>>> f = Foo()
>>> f.m()
Foo.m
>>>
>>> def m2(self):
... print "m2"
...
>>> class Bar(f.__class__): pass
...
>>> Bar.m = m2
>>>
>>> f.__class__ = Bar # f changes its camp
>>> f.m()
m2

Since we do ignore Python's internals, we do not notice that the
price to pay by f for deserting the Foo gang & joining Bar's is
an additional level of indirection for all accesses to Foo's methods.

The good news are that if f is really a rebel instance and it wants
to change camps frequently, one can use the dedicated Bar proxy class
for storing new methods at will, without affecting Foo, nor its instances.
In a sense, this also avoids the tricky solutions resulting from
peculiarities of the class/instance implementation -- a good candidate
for major changes in Python2. We won't escape these changes, unless we
manage to increase bus traffic around CNRI or so ;-)

To make a long story short, what you're trying to do is not very cool,
so perhaps you don't get truly cool answers, but still, you can do it
in Python. Fortunately, what you're asking here is not common practice,
I hope!

--
Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr
http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
Chaning instance methods [ In reply to ]
[Vladimir Marangozov, with much good advice]
> ...
> Thus we arrive at the easy & boring solution, which goes along
> these lines:
> (it's an instance hack too, but a recognized one in metaobject
> communities)
>
> >>> class Foo:
> ... def m(self):
> ... print "Foo.m"
> ...
> >>> f = Foo()
> >>> f.m()
> Foo.m
> >>>
> >>> def m2(self):
> ... print "m2"
> ...
> >>> class Bar(f.__class__): pass
> ...
> >>> Bar.m = m2
> >>>
> >>> f.__class__ = Bar # f changes its camp
> >>> f.m()
> m2

When I signed my exposition of instance-method trickery "subclassing-is-
a-lot-easier-ly y'rs", I had exactly this in mind:

class Bar(Foo):
def m(self):
print "m2"

f = Bar()

Over the years, *most* people who have asked questions akin to Jody's really
could have done this-- the *wholly* obvious thing! --from the start. Maybe
it's a "define a whole new class just to change one method?!" reluctance
left over from C++ <0.9 wink>.

catering-to-laziness-can-be-counterproductive-ly y'rs - tim
Chaning instance methods [ In reply to ]
What I was trying to do was to dynamically change the runtime behavior of an
object instance by loading the new methods from a permanent store. Since this
wasn't easy to do, I now just save off all of the attributes, kill the
old object, create a new object, and restore the attributes.

This works, but it doesn't seem to be as elegant as having the ability
to replace methods.

--
Jody Winston
Manager SeisRes
Lamont-Doherty Earth Observatory
RT 9W, Palisades, NY 10964
jody@ldeo.columbia.edu, 914 365 8526, Fax 914 359 1631

Under US Code Title 47, Sec.227(b)(1)(C), Sec.227(a)(2)(B) This email
address may not be added to any commercial mail list with out my
permission. Violation of my privacy with advertising or SPAM will
result in a suit for a MINIMUM of $500 damages/incident, $1500 for
repeats.
Chaning instance methods [ In reply to ]
Tim Peters wrote:
>
> [.me, on f being an instance of Foo, becoming an instance of Bar,
> the latter being derived directly from Foo]
> > ...
>
> When I signed my exposition of instance-method trickery "subclassing-is-
> a-lot-easier-ly y'rs", I had exactly this in mind:
>
> class Bar(Foo):
> def m(self):
> print "m2"
>
> f = Bar()
>

Careful!
We'll eventually see what you had exactly in mind if you change the last
line from

(1) f = Bar()

to

(2) f.__class__ = Bar

While (1) resets f's internal state and makes a brand new instance,
(2) preserves that state (which reflects f's past existence as an
instance of Foo) by changing only its interface to that of Bar
"on the fly". The difference is quite fundamental.

We all knew that you meant (2) in your sig, but making it slightly
more explicit does not hurt :-)

--
Vladimir MARANGOZOV | Vladimir.Marangozov@inrialpes.fr
http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252
Chaning instance methods [ In reply to ]
> To make a long story short, what you're trying to do is not very cool,
> so perhaps you don't get truly cool answers, but still, you can do it
> in Python. Fortunately, what you're asking here is not common practice,
> I hope!

Is this a truly flawed design pattern ?
I have done this for a module to replace a function with out changing the
module its self. Because I didn't control that module. Also for a class once
to allow a runtime XML type script to replace a method. Both of these can be
tricky to debug because you tend to forget what's happening. Yet a better
solution doesn't come to mind.

Possibly the module problem could be solved by creating another module and
"from XXX import *" then add my replacement function.

Darrell Gallion
Chaning instance methods [ In reply to ]
[Tim]
> When I signed my exposition of instance-method trickery "subclassing-is-
> a-lot-easier-ly y'rs", I had exactly this in mind:
>
> class Bar(Foo):
> def m(self):
> print "m2"
>
> f = Bar()

[Vladimir Marangozov]
> Careful!

I usually am <wink>.

> We'll eventually see what you had exactly in mind if you change the
> last line from
>
> (1) f = Bar()
>
> to
>
> (2) f.__class__ = Bar

Nope, I didn't have that in mind at all. Turns out (to judge from his
followup) that this is what *Jody* had in mind, but not me. Most people who
have asked this question over the years really could have done just fine
using vanilla inheritance from the start, perhaps augmented by a factory
function to choose the object's class with more care.

> While (1) resets f's internal state and makes a brand new instance,
> (2) preserves that state (which reflects f's past existence as an
> instance of Foo) by changing only its interface to that of Bar
> "on the fly". The difference is quite fundamental.

Fundamental is too weak a word, but I know what you mean <wink>. Changing
an object's class after-the-fact is not an approach I'd recommend to anyone.
That doesn't mean I don't approve of it <wink>, it's just that people far
too often start playing absurd tricks with Python's highly dynamic features
when straightforward methods work fine.

> We all knew that you meant (2) in your sig, but making it slightly
> more explicit does not hurt :-)

the-things-we-know-that-aren't-true-could-fill-a-bottomless-
ocean-to-a-depth-of-half-an-invisible-inch-ly y'rs - tim