What exactly is a method in Python?

In the code below, the object a has two functions as attributes: one is a class attribute while one is its own instance attribute.

class A:
    def foo(*args):

a = A()

def bar(*args):

a.bar = bar


What I expected was that both bar() and foo() would be methods of the object a, but, from the output of this code, it turns out that this is not the case—only foo is a method of a.

<bound method A.foo of <__main__.A object at 0x0000016F5F579AF0>>
<function bar at 0x0000016F5F5845E0>

So what exactly is a method in Python? Is it a class attribute that holds a function definition, which seems to be the case.

And why isn’t the attribute bar considered a method by Python? What exactly is the idea behind this behaviour of Python?


A method is an instance of the class method returned by, among other things, the __get__ method of a function-valued class attribute.

a.bar is an instance attribute, not a class attribute.

When looking for a.foo, Python first looks for A.foo. Having found it, it next checks if its value has a __get__ method (which, as a function value, it does.) Because A.foo is a descriptor (i.e, has a __get__ method), its __get__ method is called: a.foo is the same as A.foo.__get__(a, A). The return value is a method object, whose __call__ method calls the underlying function with the object and its own arguments. That is,

a.foo(x) == A.foo.__get__(a, A)(x)
         == A.foo(a, x)

Because a.bar is an instance attribute, the descriptor protocol is not invoked, so a.bar is the exact same object as bar.

(This is an extremely condensed version of the contents of the Descriptor HowTo Guide, in particular the section on methods. I highly recommended reading it.)

The lookup for a.bar proceeds by first looking for bar in A.__dict__. When it isn’t found, Python looks in a.__dict__, and finds a function to call: end of story.