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.
This is a very nice question.
To clear a few things:
B.p
) it said to be unbound.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.
So, why? Aren't
B.p
andB.__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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With