Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is there an official way to add methods to an instance?

1 view
Skip to first unread message

Brian Vanderburg II

unread,
Apr 3, 2008, 8:57:56 PM4/3/08
to Pytho...@python.org
I don't know if this is the correct place to send this question.

I've checked out some ways to get this to work. I want to be able to
add a new function to an instance of an object. I've tested two
different methods that cause problems with 'deleting'/garbage collection
(__del__ may never get called), but implemented one sort of hackishly
maybe that works find. I'm wondering if there is more of an official way
than mine.

1.
import new
import gc

class A:
def __del__(x):
print "Deleting"

def f(x):
print x

a = A()
a.f = new.instancemethod(a,f)
a.f() # This works
del a # Not what is expected
gc.collect() # Works, but __del__ does not get called

2.
import gc

def addmethod(self,func,name):
def wrapper(*args,**kwargs):
return func(self,*args,**kwargs)
setattr(self,name,func)

class A:
def __del__(x):
print "Deleting"

def f(x):
print x

a = A()
addmethod(a, f, "f")
a.f() # Works as expected
del a # nope
gc.collect() # Still __del__ doesn't get called

3. Slightly hackish method, maybe some problems
import gc
import weakref

def addmethod(self,func,name):
# change the value of 'self' so wrapper.func_globals will reference
the new value
self = weakref.ref(self)
def wrapper(*args,**kwargs):
return func(self(),*args,**kwargs)
setattr(self(),name,func)

class A:
def __del__(x):
print "Deleting"

def f(x):
print x

a = A()
addmethod(a, f, "f")
a.f() # Works as expected
del a
gc.collect()

With this method 'del a' does the expected most of the time, and
"Deleting" does get printed or when calling 'gc.collect()' it prints
correctly. This seems the best approach so that when 'a' is no longer
valid, the object can die instead of continuing to exitng because
wrapper.func_globals still contains a reference, but seems very hackish
an maybe problematic. I'm wondering if there is a better way?

Brian Vanderburg II

Roger Miller

unread,
Apr 3, 2008, 9:43:30 PM4/3/08
to
On Apr 3, 2:57 pm, Brian Vanderburg II <BrianVanderbu...@aim.com>
wrote:

>
> I've checked out some ways to get this to work. I want to be able to
> add a new function to an instance of an object. I've tested two
> different methods that cause problems with 'deleting'/garbage collection
> (__del__ may never get called), but implemented one sort of hackishly
> maybe that works find. I'm wondering if there is more of an official way
> than mine.
>

Maybe I'm missing something, but the boring old straightforward
approach works for me:

class A:
def __del__(self):
print "Deleting"

def f(x):
print x

a = A()
a.f = f
a.f(42)
del a

Output:
42
Deleting

Gabriel Genellina

unread,
Apr 3, 2008, 10:06:44 PM4/3/08
to pytho...@python.org
En Thu, 03 Apr 2008 21:57:56 -0300, Brian Vanderburg II
<BrianVan...@aim.com> escribió:

> I don't know if this is the correct place to send this question.
>

> I've checked out some ways to get this to work. I want to be able to
> add a new function to an instance of an object. I've tested two
> different methods that cause problems with 'deleting'/garbage collection
> (__del__ may never get called), but implemented one sort of hackishly
> maybe that works find. I'm wondering if there is more of an official way
> than mine.
>

> 1.
> import new
> import gc
>
> class A:
> def __del__(x):

> print "Deleting"
>
> def f(x):
> print x
>
> a = A()

> a.f = new.instancemethod(a,f)
> a.f() # This works
> del a # Not what is expected
> gc.collect() # Works, but __del__ does not get called

O thou of little faith, wherefore didst thou doubt?
This is the simplest and preferred way, and it works!
It's true that there is a reference cycle between a and a.f, but the
garbage collector is able to detect it and dispose of those objects
*UNLESS* any of them contains a __del__ method written in Python. See
http://docs.python.org/ref/customization.html
So this *is* the right way, and the object `a` would have been deleted if
you had not tried to witness the deletion itself... You perturbed the
system in a non trivial way just by attempting to observe it - isn't this
Quantum Mechanics applied to Python programming?

We need a side-effect triggered by __del__ but written in C... hmmm, what
about flushing a file buffer?

py> import new
py> import gc
py> import os
py>
py> class myfile(file): # just to be able to add attributes
... pass
...
py> def f(x):
... print x
...
py> a = myfile("a.aaa","w")
py> print os.stat("a.aaa").st_size # 0
0
py> a.f = new.instancemethod(f, a)
py> a.f() # This works
<open file 'a.aaa', mode 'w' at 0x00A293F8>
py> a.write("X") # a single char, should stay on write buffer
py> print os.stat("a.aaa").st_size # still 0
0
py> del a
py> print os.stat("a.aaa").st_size # still 0
0
py> gc.collect()
3
py> print os.stat("a.aaa").st_size # now 1
1

--
Gabriel Genellina

Gabriel Genellina

unread,
Apr 3, 2008, 10:08:46 PM4/3/08
to pytho...@python.org
En Thu, 03 Apr 2008 22:43:30 -0300, Roger Miller
<roger....@nova-sol.com> escribió:

This doesn't create an instance method. You can't access `a` (as `self`)
from inside f.


--
Gabriel Genellina

Peter Otten

unread,
Apr 4, 2008, 4:29:56 AM4/4/08
to
Brian Vanderburg II wrote:

> I don't know if this is the correct place to send this question.

It is.



> I've checked out some ways to get this to work. I want to be able to
> add a new function to an instance of an object. I've tested two
> different methods that cause problems with 'deleting'/garbage collection
> (__del__ may never get called), but implemented one sort of hackishly
> maybe that works find. I'm wondering if there is more of an official way
> than mine.

[snip]

I think "Try hard to avoid __del__()" is as close to an official stance as
you can get ;)

Anyway, here is one more option to add too the zoo:

>>> class A(object):
... def __init__(self, f, x):
... self._f = f
... self.x = x
... @property
... def f(self):
... return self._f.__get__(self)
... def __del__(self):
... print "deleting"
...
>>> a = A(lambda s: s.x * 2, 2)
>>> b = A(lambda s: s.x * 3, 3)
>>> a.f()
4
>>> b.f()
9
>>> del a
deleting
>>> del b
deleting

Peter

Paul Rubin

unread,
Apr 4, 2008, 5:23:36 AM4/4/08
to
Brian Vanderburg II <BrianVan...@aim.com> writes:
> I've checked out some ways to get this to work. I want to be able to
> add a new function to an instance of an object.

Ugh. Avoid that if you can. But see:

http://en.wikipedia.org/wiki/Monkey_patch

Bruno Desthuilliers

unread,
Apr 4, 2008, 7:26:00 AM4/4/08
to
Paul Rubin a écrit :

> Brian Vanderburg II <BrianVan...@aim.com> writes:
>> I've checked out some ways to get this to work. I want to be able to
>> add a new function to an instance of an object.
>
> Ugh. Avoid that if you can.

Why so ? OO is about objects, not classes, and adding methods on a
per-object basis is perfectly legitimate.

> But see:
>
> http://en.wikipedia.org/wiki/Monkey_patch

Adding methods on a per-object basis is not monkey patching (as defined
in the above article and as usually understood here) and doesn't address
the same class (no pun intended) of problems.

Bruno Desthuilliers

unread,
Apr 4, 2008, 7:50:45 AM4/4/08
to
Peter Otten a écrit :
(snip)

> Anyway, here is one more option to add too the zoo:
>
>>>> class A(object):
> ... def __init__(self, f, x):
> ... self._f = f
> ... self.x = x
> ... @property
> ... def f(self):
> ... return self._f.__get__(self)
> ... def __del__(self):
> ... print "deleting"
> ...

This is nice but requires that you know in advance how many methods
you're going to add and how they will be named (which is not a bad thing
in itself - on the contrary - but may not be what the OP is after), and
that you can add these methods at instanciation time.

A variant could be:

class A(object):
def __init__(self, x):
self.x = x

def __getattr__(self, name):
target = '_' + name
# avoids recursion
if hasattr(self, target):
func = getattr(self, target)
if hasattr(func, '__get__'):
return func.__get__(self, type(self))

# nothing found, bye...
raise AttributeError(
"%s object has no attribute %s" % (self, name)
)


a = A(21)
a._foo = lambda self: "answer is %s" % (self.x * 2)
print a.foo()

John Nagle

unread,
Apr 4, 2008, 11:25:17 AM4/4/08
to
Bruno Desthuilliers wrote:
> Paul Rubin a écrit :
>> Brian Vanderburg II <BrianVan...@aim.com> writes:
>>> I've checked out some ways to get this to work. I want to be able to
>>> add a new function to an instance of an object.
>>
>> Ugh. Avoid that if you can.
>
> Why so ? OO is about objects, not classes, and adding methods on a
> per-object basis is perfectly legitimate.

It's what professional programmers call a "l33t feature",
one not suitable for production code. Typically such features
are used by programmers with about two years experience,
trying too hard to prove that they're cool.

John Nagle

méchoui

unread,
Apr 8, 2008, 5:39:04 AM4/8/08
to
On Apr 4, 5:25 pm, John Nagle <na...@animats.com> wrote:
> Bruno Desthuilliers wrote:
> > Paul Rubin a écrit :
> >> Brian Vanderburg II <BrianVanderbu...@aim.com> writes:
> >>> I've checked out some ways to get this to work. I want to be able to
> >>> add a new function to an instance of an object.
>
> >> Ugh. Avoid that if you can.
>
> > Why so ? OO is about objects, not classes, and adding methods on a
> > per-object basis is perfectly legitimate.
>
> It's what professional programmers call a "l33t feature",
> one not suitable for production code. Typically such features
> are used by programmers with about two years experience,
> trying too hard to prove that they're cool.
>
> John Nagle

Yes, and the reason is quite obvious: if you read the code of the
class, you can't see the function. That makes it much more difficult
to understand and to debug.

bruno.des...@gmail.com

unread,
Apr 8, 2008, 7:59:41 AM4/8/08
to
On 8 avr, 11:39, méchoui <laurent.pl...@gmail.com> wrote:
> On Apr 4, 5:25 pm, John Nagle <na...@animats.com> wrote:
>
>
>
> > Bruno Desthuilliers wrote:
> > > Paul Rubin a écrit :
> > >> Brian Vanderburg II <BrianVanderbu...@aim.com> writes:
> > >>> I've checked out some ways to get this to work. I want to be able to
> > >>> add a new function to an instance of an object.
>
> > >> Ugh. Avoid that if you can.
>
> > > Why so ? OO is about objects, not classes, and adding methods on a
> > > per-object basis is perfectly legitimate.
>
> > It's what professional programmers call a "l33t feature",
> > one not suitable for production code. Typically such features
> > are used by programmers with about two years experience,
> > trying too hard to prove that they're cool.

@john:

I've ten years of experience, definitively don't care about looking
"cool" or "l33t", and sorry, but I won't buy your purely ideological
arguments. This reminds me of the lead engineer in one of my first job
forbidding using OO because he didn't get it, or some Java guy trying
to convince me that a language with dynamic typing and no access
restriction could not be used for "production code".

> > John Nagle
>
> Yes, and the reason is quite obvious: if you read the code of the
> class, you can't see the function. That makes it much more difficult
> to understand and to debug.

Then we should forbid inheritence - you don't see the inherited
functions when reading the code of the class. And we should forbid
monkey-patching, metaclasses and quite a lot of other things as well.
And also, we should go back to static typing - with dynamic typing,
you don't know by reading the signature of a function what kind of
arguments it expects.

C'mon, be serious guys. As everything else, the problem is not with
the feature, but with knowing how to properly use it and how to not
abuse it. If you don't trust the programmer, then don't use a dynamic
language. You know where to find Java and Ada...

0 new messages