How does assignment of a function as a class attribute become a method in Python?

class-method, python

Solution

You're right that this has something to do with descriptor protocol. Descriptors are how passing the receiver object as the first parameter of a method is implemented in Python. You can read more detail about Python attribute lookup from here. The following shows on a bit lower level, what is happening when you do A.func = func; A.func:

# A.func = func
A.__dict__['func'] = func # This just sets the attribute
# A.func
#   The __getattribute__ method of a type object calls the __get__ method with
#   None as the first parameter and the type as the second.
A.__dict__['func'].__get__(None, A) # The __get__ method of a function object
                                    # returns an unbound method object if the
                                    # first parameter is None.
a = A()
# a.func()
#   The __getattribute__ method of object finds an attribute on the type object
#   and calls the __get__ method of it with the instance as its first parameter.
a.__class__.__dict__['func'].__get__(a, a.__class__)
#   This returns a bound method object that is actually just a proxy for
#   inserting the object as the first parameter to the function call.

So it's the looking up of the function on a class or an instance that turns it into a method, not assigning it to a class attribute.

`classmethod` and `staticmethod` are just slightly different descriptors, classmethod returning a bound method object bound to a type object and staticmethod just returns the original function.

Problem

``` >>> class A(object): pass >>> def func(cls): pass >>> A.func = func >>> A.func <unbound method A.func> ``` How does this assignment create a method? It seems unintuitive that assignment does the following for classes: - Turn functions into unbound instance methods - Turn functions wrapped in `classmethod()` into class methods (actually, this is pretty intuitive) - Turn functions wrapped in `staticmethod()` into functions It seems that for the first, there should be an `instancemethod()`, and for the last one, there shouldn't be a wrapper function at all. I understand that these are for uses within a `class` block, but why should they apply outside of it? But more importantly, how exactly does assignment of the function into a class work? What magic happens that resolves those 3 things? Even more confusing with this: ``` >>> A.func <unbound method A.func> >>> A.__dict__['func'] <function func at 0x...> ``` But I think this is something to do with descriptors, when retrieving attributes. I don't think it has much to do with the setting of attributes here.

Original source

Related problems