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

Re: Dynamic method

10 views
Skip to first unread message

Daniel Nogradi

unread,
Jul 10, 2007, 10:07:57 AM7/10/07
to pytho...@python.org
> I have an issue I think Python could handle. But I do not have the knowledge
> to do it.
>
> Suppose I have a class 'myClass' and instance 'var'. There is function
> 'myFunc(..)'. I have to add (or bind) somehow the function to the class, or
> the instance. Any help, info or point of reference is wellcome....

How about this (note the first argument 'self' which is the instance
itself in the second case):

def method_for_instance( message ):
print message

def method_for_class( self, message ):
print message

class myClass( object ):
pass

inst = myClass( )
inst.method = method_for_instance

inst.method( 'hello' )

myClass.method = method_for_class

inst = myClass( )
inst.method( 'hello' )

Alex Popescu

unread,
Jul 10, 2007, 10:23:32 AM7/10/07
to

I think a solution may be using the new module and the function
instancemethod:

instancemethod(function, instance, class)
This function will return a method object, bound to instance, or
unbound if instance is None. function must be callable.

./alex
--
.w( the_mindstorm )p.


Bruno Desthuilliers

unread,
Jul 10, 2007, 10:46:25 AM7/10/07
to
Daniel Nogradi a écrit :

>> I have an issue I think Python could handle. But I do not have the
>> knowledge
>> to do it.
>>
>> Suppose I have a class 'myClass' and instance 'var'. There is function
>> 'myFunc(..)'. I have to add (or bind) somehow the function to the
>> class, or
>> the instance. Any help, info or point of reference is wellcome....
>
> How about this (note the first argument 'self' which is the instance
> itself in the second case):
>
>
>
> def method_for_instance( message ):
> print message
>
> def method_for_class( self, message ):
> print message
>
> class myClass( object ):
> pass
>
> inst = myClass( )
> inst.method = method_for_instance
>
> inst.method( 'hello' )

This won't work as expected:

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

def greet(self, who):
print "hello %s, my name is %s" % (who, self.name)

b = Bidule('Bruno')
b.greet = greet
b.greet('Daniel')

=>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/tmp/python-23258Nq5.py", line 10, in <module>
b.greet('Daniel')
TypeError: greet() takes exactly 2 arguments (1 given)


The point is that Python functions are descriptor objects that, when
looked up, return a (bound or unbound) method object wrapping
themselves. So to attach functions as method *on a per-instance basis*,
you either need to use new.instancemethod (as explained by Alex), or
directly use the descriptor protocol, ie:

b.greet = greet.__get__(b)
b.greet('Daniel')
=> hello Daniel, my name is Bruno

Note that you don't need this to attach a function as method to a class
- the descriptor protocol will then JustWork(tm).

HTH

Daniel Nogradi

unread,
Jul 11, 2007, 3:17:38 AM7/11/07
to Bruno Desthuilliers, pytho...@python.org


Well, I thought the name method_for_instance and method_for_class are
clear enough that if you want to 'attach' one of them to an instance
then method_for_instance should be used :)
And method_for_instance has only one argument and so will work as in
the example I gave (I guess you just overlooked them).


> The point is that Python functions are descriptor objects that, when
> looked up, return a (bound or unbound) method object wrapping
> themselves. So to attach functions as method *on a per-instance basis*,
> you either need to use new.instancemethod (as explained by Alex), or
> directly use the descriptor protocol, ie:
>
> b.greet = greet.__get__(b)
> b.greet('Daniel')
> => hello Daniel, my name is Bruno
>
> Note that you don't need this to attach a function as method to a class
> - the descriptor protocol will then JustWork(tm).


I agree that in general the solution explained by Alex and you is better.

Bruno Desthuilliers

unread,
Jul 11, 2007, 5:00:37 AM7/11/07
to
Daniel Nogradi a écrit :
(snip)

>> > def method_for_instance( message ):
>> > print message
>> >
>> > class myClass( object ):
>> > pass
>> >
>> > inst = myClass( )
>> > inst.method = method_for_instance
>> >
>> > inst.method( 'hello' )
(snip)

>> This won't work as expected:
>>
>> class Bidule(object):
>> def __init__(self, name):
>> self.name = name
>>
>> def greet(self, who):
>> print "hello %s, my name is %s" % (who, self.name)
>>
>> b = Bidule('Bruno')
>> b.greet = greet
>> b.greet('Daniel')
>>
>> =>
>> Traceback (most recent call last):
>> File "<stdin>", line 1, in <module>
>> File "/tmp/python-23258Nq5.py", line 10, in <module>
>> b.greet('Daniel')
>> TypeError: greet() takes exactly 2 arguments (1 given)
>
>
> Well, I thought the name method_for_instance and method_for_class are
> clear enough that if you want to 'attach' one of them to an instance
> then method_for_instance should be used :)
> And method_for_instance has only one argument and so will work as in
> the example I gave (I guess you just overlooked them).

I didn't overlooked anything. *You* did. Your "method_for_instance" is
*not* a method - it's a function. It's easy to check this out:

>>> class Toto(object):
... def truc(self):
... print "%s.truc" % self
...
>>> def chose():
... print "in chose, no 'self' here"
...
>>> t = Toto()
>>> t.truc
<bound method Toto.truc of <__main__.Toto object at 0xb7925c0c>>
>>> t.chose = chose
>>> t.chose
<function chose at 0xb7db541c>
>>>

The whole point of methods is that they do have access to the object
(instance or class) - which is not the case of your
"method_for_instance". Please reread the following:

>
>> The point is that Python functions are descriptor objects that, when
>> looked up, return a (bound or unbound) method object wrapping
>> themselves. So to attach functions as method *on a per-instance basis*,
>> you either need to use new.instancemethod (as explained by Alex), or
>> directly use the descriptor protocol, ie:
>>
>> b.greet = greet.__get__(b)
>> b.greet('Daniel')
>> => hello Daniel, my name is Bruno
>>
>> Note that you don't need this to attach a function as method to a class
>> - the descriptor protocol will then JustWork(tm).
>
>
> I agree that in general the solution explained by Alex and you is better.

They are not "better" - they are correct.

HTH

Lawrence Oluyede

unread,
Jul 16, 2007, 8:41:43 AM7/16/07
to
Bruno Desthuilliers <bruno.42.de...@wtf.websiteburo.oops.com>
wrote:

> > I agree that in general the solution explained by Alex and you is better.
>
> They are not "better" - they are correct.

Another way is to use the 'types' module:

In [1]: class T(object):
...: pass
...:

In [2]: t = T()

In [3]: import types

In [5]: types.MethodType?
Type: type
Base Class: <type 'type'>
String Form: <type 'instancemethod'>
Namespace: Interactive
Docstring:
instancemethod(function, instance, class)

Create an instance method object.


In [10]: m = types.MethodType(lambda self: 3, t, T)

In [11]: t.m = m

In [12]: t.m()
Out[12]: 3


--
Lawrence, oluyede.org - neropercaso.it
"It is difficult to get a man to understand
something when his salary depends on not
understanding it" - Upton Sinclair

Bruno Desthuilliers

unread,
Jul 16, 2007, 10:29:16 AM7/16/07
to
Lawrence Oluyede a écrit :

> Bruno Desthuilliers <bruno.42.de...@wtf.websiteburo.oops.com>
> wrote:
>>> I agree that in general the solution explained by Alex and you is better.
>> They are not "better" - they are correct.
>
> Another way is to use the 'types' module:

True - and that's somewhat cleaner since it doesn't expose the internals
of the descriptor protocol. OTHO, it can lead to strange results with
callables not implementing the descriptor protocol:

class MyCallable(object):


def __init__(self, name):
self.name = name

def __call__(self):
print self.name

fun = MyCallable('gizmo')

class Foo(object):
pass

f = Foo()
f.fun = types.MethodType(fun, f, Foo)
f.fun()

=>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>

File "/tmp/python-17437zds.py", line 16, in <module>
f.fun()
TypeError: __call__() takes exactly 1 argument (2 given)

Daniel

unread,
Jul 16, 2007, 5:30:32 PM7/16/07
to
Bruno Desthuilliers wrote:
>
> > Another way is to use the 'types' module:
>
> True - and that's somewhat cleaner since it doesn't expose the internals
> of the descriptor protocol. OTHO, it can lead to strange results with
> callables not implementing the descriptor protocol:

<snip>

Actually, the result is not "strange" at all if you understand what's
going. The reason you got an exception is because your __call__()
method only takes a single argument (the implicit self argument, which
is the instance of MyCallable). However, if you define it with two
arguments it works just fine:

>>> class MyCallable(object):
... def __call__(self, inst):
... print self, inst
...
>>> class Foo(object):
... pass
...
>>> fun = MyCallable()


>>> f = Foo()
>>> f.fun = types.MethodType(fun, f, Foo)
>>> f.fun()

<__main__.MyCallable object at 0x648d0> <__main__.Foo object at
0x64810>
>>>

~ Daniel

Bruno Desthuilliers

unread,
Jul 17, 2007, 3:46:57 AM7/17/07
to
Daniel a écrit :

> Bruno Desthuilliers wrote:
>>> Another way is to use the 'types' module:
>> True - and that's somewhat cleaner since it doesn't expose the internals
>> of the descriptor protocol. OTHO, it can lead to strange results with
>> callables not implementing the descriptor protocol:
>
> <snip>
>
> Actually, the result is not "strange" at all if you understand what's
> going.

Don't worry, *I* do exactly know what's going on !-)

But we all here know that newbies already tend to get confused (or at
least perplexed) by the error message they get when they forgot the self
argument in methods definition... FWIW, I did experiment this situation
a couple years ago, whith a coworker that, while proficient with C and
Perl, was somewhat new to Python. He banged his head against the wall
for near an hour before asking for help, and it took me quite a couple
of minutes to figure out what was going wrong.

0 new messages