5

Given a class A I can simply add an instancemethod a via

def a(self):
 pass
A.a = a

However, if I try to add another class B's instancemethod b, i.e. A.b = B.b, the attempt at calling A().b() yields a

TypeError: unbound method b() must be called with B instance as first argument (got nothing instead)

(while B().b() does fine). Indeed there is a difference between

A.a -> <unbound method A.a>
A.b -> <unbound method B.b> # should be A.b, not B.b

So,

  • How to fix this?
  • Why is it this way? It doesn't seem intuitive, but usually Guido has some good reasons...

Curiously enough, this no longer fails in Python3...

asked Sep 10, 2013 at 7:36
2

2 Answers 2

6

Let's:

class A(object): pass
class B(object):
 def b(self): 
 print 'self class: ' + self.__class__.__name__

When you are doing:

A.b = B.b

You are not attaching a function to A, but an unbound method. In consequence python only add it as a standard attribute and do not convert it to a A-unbounded method. The solution is simple, attach the underlying function :

A.b = B.b.__func__
print A.b
 # print: <unbound method A.b>
a = A()
a.b()
 # print: self class: A

I don't know all the difference between unbound methods and functions (only that the first contains the second), neither how all of that work internally. So I cannot explain the reason of it. My understanding is that a method object (bound or not) requires more information and functionalities than a functions, but it needs one to execute.

I would agree that automating this (changing the class of an unbound method) could be a good choice, but I can find reasons not to. It is thus surprising that python 3 differs from python 2. I'd like to find out the reason of this choice.

answered Sep 10, 2013 at 8:30
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I feared that would have been more complicated!
2

When you take the reference to a method on a class instance, the method is bound to that class instance.

B().b is equivalent to: lambda *args, **kwargs: b(<B instance>, *args, **kwargs)

I suspect you are getting a similarly (but not identically) wrapped reference when evaluating B.b. However, this is not the behavior I would have expected.

Interestingly:

A.a = lambda s: B.b(s)
A().a()

yields:

TypeError: unbound method b() must be called with B instance as first argument (got A instance instead)

This suggests that B.b is evaluating to a wrapper for the actual method, and the wrapper is checking that 'self' has the expected type. I don't know, but this is probably about interpreter efficiency.

It's an interesting question though. I hope someone can chime in with a more definitive answer.

answered Sep 10, 2013 at 7:55

2 Comments

Curiously all works fine with Python3, where (see e.g. answers here) this confusing concept of unbound methods has been abandoned
Huh, as Juh_ showed, one simply needs to use B.b.__func__. Still a weird "feature", no wonder it's no longer in Python3...

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.