No self parameter passed in overrided python class' method

Question:

Take a look at this code:

class SomeClass:
    def __init__(self):
        self._value = 'Hello, World!'

    def func(self):
        return self._value

instance = SomeClass()

print(instance.func())

def new_func(self):
    return self._value + '!!!!!'

instance.func = new_func

print(instance.func())

This code creates an instance of SomeClass, calls original func, then overrides this function and calls it again.
I want to override a function in a single instance of a class, but not in the entire class so all the instances got the function overrided.
I expect this code to print:

Hello, World!
Hello, World!!!!!

But it prints:

Hello, World!
Traceback (most recent call last):
  File "test.py", line 17, in <module>
    print(instance.func())
TypeError: new_func() missing 1 required positional argument: 'self'

The self argument is not passed to the overrided function. Why? Aren’t SomeClass.func(instance) and instance.func() the same?

How to override the function correctly so I can access the self argument in the overrided function?

Asked By: g00dds

||

Answers:

Well let’s see

The func of the original instance has type bound method

SomeClass().func

# <bound method SomeClass.func of <__main__.SomeClass object at 0x7f293e1cb3d0>>

and your func has type function

instance.func

# <function __main__.new_func(self)>

The idea is to get a new method of some new class like this:

class SomeNewClass:
    def new_func(self):
        return self._value + '!!!!!'

instance.func = SomeNewClass().new_func
instance.func

# <bound method SomeNewClass.new_func of <__main__.SomeNewClass object at 0x7f293e2825d0>>

But as you see, this is a method of the new class 🙁

By the way, if you don’t initialize the class, the type will change to function:

class SomeNewClass:
    def new_func(self):
        return self._value + '!!!!!'

instance.func = SomeNewClass.new_func
instance.func

# <function __main__.SomeNewClass.new_func(self)>

So the next idea is to change the method before the initialization of a class:

SomeClass.func = new_func
instance = SomeClass()
instance.func

# <bound method new_func of <__main__.SomeClass object at 0x7f29441e4350>>

Seems good. The result based on your code:

class SomeClass:
    def __init__(self):
        self._value = 'Hello, World!'

    def func(self):
        return self._value

instance = SomeClass()

print(instance.func())

def new_func(self):
    return self._value + '!!!!!'

SomeClass.func = new_func
instance = SomeClass()

print(instance.func())

# Hello, World!
# Hello, World!!!!!!

If you need to change the method of an instance and not of the class. There is also a way to do that using types.MethodType:

import types

class SomeClass:
    def __init__(self):
        self._value = 'Hello, World!'

    def func(self):
        return self._value

instance = SomeClass()

print(instance.func())

def new_func(self):
    return self._value + '!!!!!'

instance.func = types.MethodType(new_func, instance)

print(instance.func())

# Hello, World!
# Hello, World!!!!!!
Answered By: zalevskiaa
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.