Confused with methods

Alex Martelli aleaxit at yahoo.com
Sun Feb 6 11:15:23 EST 2005


jfj <jfj at freemail.gr> wrote:

> I don't understand.
> We can take a function and attach it to an object, and then call it
> as an instance method as long as it has at least one argument:
> 
> #############
> class A:
>    pass
> 
> def foo(x):
>    print x
> 
> A.foo = foo
> a=A()
> a.foo()
> #############

Right.  If you want to understand how this happens, look at the
foo.__get__ special method, which makes foo (like every other function)
a *descriptor*.  One you've set foo as an attribute of class A, the
access a.foo calls foo.__get__(a, A) which returns an object of class
"instance method".


> However this is not possible for another instance method:
> 
> ############
> class A:
>   pass
> 
> class B:
>   def foo(x,y)
>       print x,y
> 
> b=B()
> A.foo = b.foo

What you're assigning here as an attribute of class A is not a
descriptor: it does not have a special method __get__.

> a=A()
> 
> # error!!!
> a.foo()

Since A.foo does not have a method __get__, it's just returned when you
access it (on instance a).  It's already an instance method -- but it's
bound to instance b of class B.

> ##############
> 
> Python complains that 'foo() takes exactly 2 arguments (1 given)'.
> But by calling "b.foo(1)" we prove that it is indeed a function which takes
> exactly one argument.

Calling something does not prove it's a function.  Python has a lot of
callable objects, and functions are just one callable types.  So, you're
mistaken if you think that by calling (whatever) you prove said whatever
is a function (of any kind).

The error message you're seeing does come from a function -- the im_func
attribute of b.foo, which is a function foo taking two arguments
(created and set into class B by the "def foo(x, y):" above, even though
you forgot the colon I'm sure you intended to type it).

 
> Isn't that inconsistent?

That Python has many callable types, not all of which are descriptors?
I don't see any inconsistency there.  Sure, a more generalized currying
(argument-prebinding) capability would be more powerful, but not more
consistent (there's a PEP about that, I believe).

If what you want is to focus on the error message, you can completely
separate it from the idea of having set b.foo as an attribute of class
A.  Just try calling b.foo() without arguments and you'll get exactly
the same error message -- from the underlying function foo, which is
also b.foo.im_func (the function's FIRST argument is bound to
b.foo.im_self, which is b; whence the error being about function foo
taking exactly two arguments and only one having been given).


Alex



More information about the Python-list mailing list