Home > Enterprise >  Class methods redefining, 'self' argument problems
Class methods redefining, 'self' argument problems

Time:03-01

I want to implement such case - i have a class, there i have some variable which contains external method, in some situations method can be redefined. It may looks like:

def my_print(self):
    print('my print is called')

class example():
    some_method = my_print

    def __init__(self):
        self.some_method()

    def test(self):
        self.some_method()

So, it works:

a = example()
a.test()

The result looks like:

my print is called (from init)
my print is called (from test)

But in case redefine some_method:

a = example()
a.some_method = my_print
a.test()

It returns a error, which says that i need to give self as argument in the line self.some_method():

TypeError: my_print() missing 1 required positional argument: 'self'

Why does it happen and maybe someone knows trick to solve this problem?

CodePudding user response:

It's a subtlety of what . does in Python. It both looks up the method on the class, and binds the method to the instance. This binding is what provides the class instance implicitly as the first argument, self. Look at this:

>>> print(example.some_method)
<function my_print at 0x7f66c4129f30>
>>> print(a.some_method)
<bound method my_print of <test.example object at 0x7f66c41c2290>>

However, when the method is not found on the class but on the instance, the binding does not happen:

>>> a = example()
my print is called
>>> a.some_method = my_print
>>> print(a.some_method)
<function my_print at 0x7f66c4129f30>

This is stated in the Python reference:

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.

The solution, then, is to make sure that the method is always an attribute of the class instance, and call it accordingly:

class example():

    def __init__(self):
        self.some_method = my_print
        self.some_method(self)

    def test(self):
        self.some_method(self)

Looks weird, works great.

If you want the method to always be an attribute of the class instead, consider using inheritance.

  • Related