Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instancemethod or function?

Tags:

Today I defined a class just for testing what comes next:

class B(object):     def p(self):         print("p") 

And later I did this:

>>> type(B.__dict__['p']) <type 'function'> >>> type(B.p) <type 'instancemethod'> 

So, why? Aren't B.p and B.__dict__['p'] the same object?

My astonishment just increased when a I tried this:

>>> B.__dict__['p'] <function p at 0x3d2bc80> >>> type(B.__dict__['p']) <type 'function'> 

Ok, so far so good, the type is in both results function, but when I tried:

>>> B.p <unbound method B.p> >>> type(B.p) <type 'instancemethod'> 

What?!, Why? unbound method and instancemethod? Are those the same? Why two different names?

Well, It seems python is full of surprises!

And this is the python I'm using:

Python 2.7.4 (default, Sep 26 2013, 03:20:26)  [GCC 4.7.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. 
like image 978
Alvaro Fuentes Avatar asked Dec 19 '13 14:12

Alvaro Fuentes


2 Answers

This is a very nice question.

To clear a few things:

  1. When a method name is accessed through its class (e.g. B.p) it said to be unbound.
  2. When otherwise the method is accessed through its class' instance (e.g. B().p) is said to be bound.

The main difference between bound and unbound is that if bound, the first argument will be implicitly the instance of the calling class. That's why we have to add self as first argument to every method when we define it. When unbound, you'll have to pass explicitly the instance of the class which you want to apply the logic of the method.

For example:

class B(object):     def foo(self):         print 'ok'  >>>B().foo() ok >>>B.foo() Exception, missing an argument. >>>B.foo(B()) ok 

This is a basic explanation of bound and unbound. Now regarding to the __dict__ weirdness. Any object in Python can define a __get__ and __set__ method, which control the access to them when they're attributes in a class. These are called descriptors.

In simpler words, when you access an attribute (property) of a class by its instance or class, Python doesn't return the object directly, instead it calls a __get__ or __set__ method that in turn returns a convenient object to work with.

Functions in Python override this __get__ method. So when you issue B.foo or B().foo it returns an instancemethod type, which is a wrapper to function type (a wrapper that pass self as first argument implicitly). When you access the function through the raw class' dictionary, there's no call to __get__ because you're not accessing them as a property of a class, hence the return value to be a raw function.

There's a lot to say about this topic, I tried to give a very simple answer to such a clever topic. You can find definitive information on Guido's blog article The Inside Story on New-Style Classes, very recommended.

UPDATE: About your last example:

>>> B.p <unbound method B.p> >>> type(B.p) <type 'instancemethod'> 

Notice that in Python's interpreter >>>B.p doesn't actually print the type of the object, instead it prints the object's __repr__ method. You can check that by doing >>>print B.p.__repr__() and seeing that its the same result :)

Python's is full of indirection and delegation, that's what makes it so flexible.

Hope this clarify things a bit.

like image 160
Paulo Bu Avatar answered Oct 18 '22 23:10

Paulo Bu


So, why? Aren't B.p and B.__dict__['p'] the same object?

No. Attribute access is magic; when a user-defined method is accessed by attribute name, either a bound method or unbound method is returned (This changes in Python 3; there are no longer unbound methods at all; you get a regular function if you access an instance method through the class object); accessing directly through the dict bypasses this magic.

What?!, Why? unbound method and instancemethod?, Are those the same? Why two different names?

A user-defined method is of type instancemethod unless it was defined with the @classmethod or @staticmethod decorator. It can either be a bound instancemethod (when accessed as an attribute of an instance) or an unbound method (when accessed as an attribute of the class).

(See the "User-defined methods" section of http://docs.python.org/2/reference/datamodel.html for an explanation.)

like image 23
Wooble Avatar answered Oct 19 '22 01:10

Wooble