Why is binding a class instance method different from binding a class method?

I was reading the python docs and stumbled upon the following lines:

It is also important to note that user-defined functions which are attributes of a class instance are not converted to bound methods; this only happens when the function is an attribute of the class.

Please, someone explain what does that mean in plain english.

I’m going to introduce some shorthand notation:

let ‘user-defined functions‘ be denoted by f,

let ‘class instance‘ be denoted by ci while class denoted simply by c. Obviously(?), ci = c(), with some abuse of notation.

Also, allow membership statements to be recast in simple set notation eg ‘user-defined functions which are attributes of a class instance‘ in shorthand is ‘vf: fεa(ci)‘, where v: ‘for all‘ and where ‘a‘ is the shorthand for (set of) attributes (eg of a class or class instance) and ‘ε’ denotes the set membership function.

Also, the process of binding a function is described in shorthand by ci.f(*args) or c.f(*args) => f(ci, *args) or f(c, *args) (the former referring to an instance method call while the later referring to a class method call)

Using the newly introduced shorthand notation, does the quote from the docs imply that

vf: fεa(c), c.f(*args) => f(c, *args) is a true statement

while

vf: fεa(ci), ci.f(*args) => f(ci, *args) is false?

Answer

Setting a User Defined Method to be an Attribute of Class, The Wrong Way

Consider the following example class A and function f:

class A:
    pass

def f(self):
    print("I'm in user-defined function")

a = A()

The function f is defined separately and not inside the class.

Let’s say you want to add function f to be an instance method for a object.

Adding it, by setting f as a attribute, won’t work:

import types

class A:
    pass

def f(self):
    print("I'm in user-defined function")

a = A()
a.f = f

# <function f at 0x000002D81F0DED30>
print(a.f)

# TypeError: f() missing 1 required positional argument: 'self'
# a.f()

Because function f is not bound to the object a.

That is why when calling a.f() it shall raise an error regarding the missing argument (if f has been bounded to a, that object a was the missing argument self).

This part is what the docs referred at:

It is also important to note that user-defined functions which are attributes of a class instance are not converted to bound methods.

Of course, all this has not to happen if function f has been defined inside class A, that’s what the following part from the docs states:

…this only happens when the function is an attribute of the class.

Setting a User Defined Method to be an Attribute of Class, The Right Way

To add function f to object a you should use:

import types

class A:
    pass

def f(self):
    print("I'm in user-defined function")

a = A()

a.f = types.MethodType( f, a )

# <bound method f of <__main__.A object at 0x000001EDE4768E20>>
print(a.f)

# Works! I'm in user-defined function
a.f()

Which bounds the user-defined method f to instance a.

Leave a Reply

Your email address will not be published. Required fields are marked *