0

Assume class Man is defined within third-party file, which is not able to be directly modified:

class Man:
    def __init__(self, name):
        self.name = name
    
    def greet(self):
        print(f"Hi, I'm {self.name}.")

I want some objects behave differently when called greet method. For example, they speak "Hello" rather than "Hi" when greeting. There are codes calling greet in the third-party file, and I want to change their behaviour. Here's what I did:

def another_greet(self):
    print(f"Hello, I'm {self.name}.")

man = Man("Sue")
man.greet()  # output: "Hi, I'm Sue"
man.greet = another_greet
man.greet()  # TypeError: another_greet() missing 1 required positional argument: 'self'
man.greet(man)  # output "Hello, I'm Sue"

However, after the replacing man.greet = another_greet, it's not able to call greet by man.greet(), since the self parameter cannot be automatically passed with the object itself. I have to call the method with extra explicit parameter, like man.greet(man), but I can't change the man.greet()s in the third party files.

Intuitively I guess there's a way to do it, maybe some tricks on another_greet is needed?

Inheritance does solve the question at a certain level. But in practice the object is not declared by me but given by third-party function, so I cannot initialize it as ModifiedMan. Anyway, it's true I can replace it with another ModifiedMan object, taking all its original parameters, yet the practical class is more complex the simply Man and not easy to initialize, doing this introduces much redundant code.

Flicic Suo
  • 125
  • 6

2 Answers2

2

Through inheritance, you can extend the functionality of the Man class.

Define your own class, and inherit the Man class. Then, you can override the greet() method, and instances of your new class will use the new definition of greet()

class Man:
    def __init__(self, name):
        self.name = name
    
    def greet(self):
        print(f"Hi, I'm {self.name}.")

# This is the class that is inheriting Man.
class ModifiedMan(Man):

    # Redefine the method that you want to override
    def greet(self):
        print(f"Hello, I'm {self.name}."

You can read more about inheritance and polymorphism here.

Kevin Hoopes
  • 417
  • 7
  • 1
    Inheritance does solve the question at a certain level. But in practice the object is not declared by me but given by third-party function, so I cannot initialize it as ```ModifiedMan```. Anyway, it's true I can replace it with another ```ModifiedMan``` object, taking all its original parameters, yet the practical class is more complex the simply ```Man``` and not easy to initialize, doing this introduces much redundant code. – Flicic Suo Aug 20 '20 at 08:21
  • In that case, I think Nishant's answer is more what you are looking for. – Kevin Hoopes Aug 20 '20 at 13:59
2

In your solution, self is not being passed automatically because another_greet is an unbound function unlike greet which is a bound method. Python: Bind an Unbound Method? has some solutions to this problem.

To solve your original problem, inheritance seems like the proper way to go about; but it also needs the library to provide all the facilities to extend it (from a "usage" perspective). If that is not the case -- and you are doing it for a non-production usecase -- you can also do "monkey patching" like this:

from somewhere import Man

def greet(self):
    print(f"Hello, I'm {self.name}.")

Man.greet = greet

I am replacing the function at the class level itself so it becomes a bound method. I do this a lot in my development environment (like adding logging functionality etc.).

Nishant
  • 17,152
  • 14
  • 56
  • 80