Dynamic class methods misunderstanding

B

Bill Mill

Hello all,

I have a misunderstanding about dynamic class methods. I don't expect
this behavior:

In [2]: class test:
...: def __init__(self, method):
...: self.method = method
...: self.method()
...:

In [3]: def m(self): print self
...:

In [4]: test(m)
---------------------------------------------------------------------------
exceptions.TypeError Traceback (most recent call
last)

/cygdrive/c/Documents and Settings/Wmill/<console>

/cygdrive/c/Documents and Settings/Wmill/<console> in __init__(self, method)

TypeError: m() takes exactly 1 argument (0 given)
-----------------------------------------------------------------------------

Why doesn't m get the implicit self parameter in the self.method()
call? How would I make it a proper member of the class, so that a
self.method() call would work with the above "m" function?

Peace
Bill Mill
bill.mill at gmail.com
 
F

F. Petitjean

Le Fri, 28 Jan 2005 10:20:30 -0500, Bill Mill a écrit :
Hello all,

I have a misunderstanding about dynamic class methods. I don't expect
this behavior:

In [2]: class test:
...: def __init__(self, method):
...: self.method = method
...: self.method()
...:

In [3]: def m(self): print self
...:

In [4]: test(m)
---------------------------------------------------------------------------
exceptions.TypeError Traceback (most recent call
last)

/cygdrive/c/Documents and Settings/Wmill/<console>

/cygdrive/c/Documents and Settings/Wmill/<console> in __init__(self, method)

TypeError: m() takes exactly 1 argument (0 given)
-----------------------------------------------------------------------------

Why doesn't m get the implicit self parameter in the self.method()
call? How would I make it a proper member of the class, so that a
self.method() call would work with the above "m" function?
The "def m(self):" was not properly indented. So here, "m" is a module level
function, not a method of your class.
 
D

Diez B. Roggisch

Why doesn't m get the implicit self parameter in the self.method()
call? How would I make it a proper member of the class, so that a
self.method() call would work with the above "m" function?

Use new.instancemethod:

import new

class Test:
def __init__(self, method):
self.m = new.instancemethod(method, self, Test)

def m(self):
print self

Test(m).m()
 
K

Kamilche

I see what you're attempting to do. However, your code, if it DID run,
would result in a method being added to the object, not the object's
class! Modify the class itself, not the object, as follows:

|class Test:
| def __init__(self):
| self.method()
|
|def m(self):
| print self
|
|setattr(Test, 'method', m)
|Test()
 
H

Hans Nowak

Bill said:
Hello all,

I have a misunderstanding about dynamic class methods. I don't expect
this behavior:

In [2]: class test:
...: def __init__(self, method):
...: self.method = method
...: self.method()
...:

In [3]: def m(self): print self
...: [...]

TypeError: m() takes exactly 1 argument (0 given)
-----------------------------------------------------------------------------

Why doesn't m get the implicit self parameter in the self.method()
call? How would I make it a proper member of the class, so that a
self.method() call would work with the above "m" function?

m is a function. When you assign it to self.method, it's still a
function. You don't create a new method that way; all you have is a new
attribute called 'method' containing the function.

To add m as a new method to the *class*, do this:
.... def __init__(self, method):
.... self.__class__.method = method
.... self.method()
....
To add m as a new method to the *instance*, use new.instancemethod, as
Diez B. Roggisch already pointed out.

HTH,
 
B

Bill Mill

Diez,

Use new.instancemethod:

import new

class Test:
def __init__(self, method):
self.m = new.instancemethod(method, self, Test)

Beautiful! thank you very much. Looking into the "new" module in
python 2.4, that's equivalent to:

self.m = type(self.__init__)(method, self, Test)

I didn't know that you could call types to create another type.

Peace
Bill Mill
bill.mill at gmail.com
 
B

Bill Mill

Hans,


m is a function. When you assign it to self.method, it's still a
function. You don't create a new method that way; all you have is a new
attribute called 'method' containing the function.

I figured as much; I just didn't know how to add it as a method.
To add m as a new method to the *class*, do this:

... def __init__(self, method):
... self.__class__.method = method
... self.method()
...
<__main__.test instance at 0x0192ED78>
<__main__.test instance at 0x0192ED78>

When I run it, I only get one call to m, which is how I would expect
python to work; I assume the double printing here is a typo?
To add m as a new method to the *instance*, use new.instancemethod, as
Diez B. Roggisch already pointed out.

Thanks, you helped me understand it a lot.

Peace
Bill Mill
bill.mill at gmail.com
 
B

Bill Mill

Kamilche,


I see what you're attempting to do. However, your code, if it DID run,
would result in a method being added to the object, not the object's
class! Modify the class itself, not the object, as follows:

|class Test:
| def __init__(self):
| self.method()
|
|def m(self):
| print self
|
|setattr(Test, 'method', m)
|Test()

beautiful; so this appears to be equivalent to the __class__ method
that Hans suggested.

Thanks a lot.

Peace
Bill Mill
bill.mill at gmail.com
 
H

Hans Nowak

Bill said:
When I run it, I only get one call to m, which is how I would expect
python to work; I assume the double printing here is a typo?

Actually, no. I'm using the interactive interpreter, so doing test(m)
results in two lines: the first one is printed by m, the second one is
the __repr__ of the test instance that was created, displayed by the
interpreter. Compare:
<__main__.test instance at 0x0192ED78>
 
B

Bill Mill

Actually, no. I'm using the interactive interpreter, so doing test(m)
results in two lines: the first one is printed by m, the second one is
the __repr__ of the test instance that was created, displayed by the
interpreter. Compare:

<__main__.test instance at 0x0192ED78>

d'oh; that's how I ran it. Thanks a lot.
 
C

Craig Ringer

Beautiful! thank you very much. Looking into the "new" module in
python 2.4, that's equivalent to:

self.m = type(self.__init__)(method, self, Test)

I didn't know that you could call types to create another type.

Well, a type is essentially a class (in the OOP sense, not the python-
specific classobj sense). You can call a type or class to create an
instance of that class or type. Here, you call the 'instancemethod' type
to create an instance of type 'instancemethod'. Makes sense ... in
hindsight.
 
T

Terry Reedy

Kamilche said:
I see what you're attempting to do. However, your code, if it DID run,
would result in a method being added to the object, not the object's
class! Modify the class itself, not the object, as follows:

|class Test:
| def __init__(self):
| self.method()
|
|def m(self):
| print self
|
|setattr(Test, 'method', m)

# this is a longwinded way to say
Test.method = m

setattr is for when you do *not* know the attribute name at coding time but
will have it in a string at run time, as in

methodname = 'method'
.......# some time later
setattr(Test, methodname, m)

Sometime Python makes things easier than people are initially willing to
believe ;-)

Terry J. Reedy
 
B

Bill Mill

# this is a longwinded way to say
Test.method = m

That is the blindingly simple method that I wanted. I didn't know
before that I wanted it, but I'm sure of it now. Thank you very much,
terry.
setattr is for when you do *not* know the attribute name at coding time but
will have it in a string at run time, as in

methodname = 'method'
......# some time later
setattr(Test, methodname, m)

Sometime Python makes things easier than people are initially willing to
believe ;-)

I felt like there had to be a simpler solution.

Peace
Bill Mill
bill.mill at gmail.com
 
A

Alex Martelli

Bill Mill said:
Beautiful! thank you very much. Looking into the "new" module in
python 2.4, that's equivalent to:

self.m = type(self.__init__)(method, self, Test)

Another approach with the same result is to exploit the fact that a
function is a descriptor:

self.m = method.__get__(self, Test)


Alex
 
C

Christos TZOTZIOY Georgiou

Another approach with the same result is to exploit the fact that a
function is a descriptor:

self.m = method.__get__(self, Test)

Almost true; not all builtin functions are descriptors though.

..>> import new
..>> f= new.instancemethod(divmod, 7, object)
..>> map(f, range(1,10,2))
[(7, 0), (2, 1), (1, 2), (1, 0), (0, 7)]
..>> f= divmod.__get__(7)

Traceback (most recent call last):
File "<pyshell#16>", line 1, in -toplevel-
f= divmod.__get__(7)
AttributeError: 'builtin_function_or_method' object has no attribute
'__get__'

I didn't run an extensive test, but it's possible that all builtin
functions are not descriptors.
 
A

Alex Martelli

Christos TZOTZIOY Georgiou said:
Almost true; not all builtin functions are descriptors though. ...
AttributeError: 'builtin_function_or_method' object has no attribute
'__get__'

I didn't run an extensive test, but it's possible that all builtin
functions are not descriptors.

Indeed, many callables (builtin-functions, types, ...) are not
descriptors, so you can't call __get__ on them. But then, many
descriptors (statimethods, classmethods, ...) are not callable, so you
can't pass them as the first argument to instancemethod. Not having any
indication of what the author of class Test means to pass in as
"method", it's not clear whether Test(divmod) or Test(staticmethod(foo))
is more likely to be of interest to them as a use case. If ``method''
is a function, which is both a descriptor AND callable, either approach
works; otherwise, one has to pick an approach, or try both approaches
with a try/except.


Alex
 

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

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top