Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I replace an existing method of an object in Python? [duplicate]

Tags:

Q: Is there a way to alter a method of an existing object in Python (3.6)? (By "method" I mean a function that is passed self as an argument.)


Example

Let's say I have a class Person having some very useful method SayHi():

class Person(object):
    Cash = 100
    def HasGoodMood(self):
        return self.Cash > 10

    def SayHi(self):
        if self.HasGoodMood():
            print('Hello!')
        else:
            print('Hmpf.')

>>> joe = Person()
>>> joe.SayHi()
Hello!

As you can see, the response of the person depends on their current mood computed by the method HasGoodMood(). A default person has good mood whenever they have more than 10$ cash on them.

I can easily create a person who does not care about the money and is happy all the time:

>>> joe.HasGoodMood = lambda: True
>>> joe.SayHi()
Hello!
>>> joe.Cash = 0
>>> joe.SayHi()
Hello!

Cool. Notice how Python knows that when using the original implementation of HasGoodMood, it passes silently self as the first argument, but if I change it to lambda: True, it calls the function with no arguments. The problem is: What if I want to change the default HasGoodMood for another function which would also accept self as a parameter?

Let's continue our example: what if I want to create a greedy Person who is only happy if they have more than 100$ on them? I would like to do something like:

>>> greedy_jack = Person()
>>> greedy_jack.HasGoodMood = lambda self: self.Cash > 100
TypeError: <lambda>() missing 1 required positional argument: 'self'

Unfortunately, this does not work. Is there some other way to change a method?


Disclaimer: The above example is just for demonstration purposes. I know that I could use inheritance or keep a cash threshold as a property of the Person. But that is not the point of the question.

like image 691
Jan Kukacka Avatar asked Sep 12 '18 10:09

Jan Kukacka


People also ask

Can you overwrite an object in Python?

So if you want to change an object that is not immutable (like a list), that should work out-of-the-box in Python. However, you cannot really generally overwrite objects in Python. Creating a new object will use free memory. And memory that is in use will only be garbage-collected once it has no references to it.

How do you duplicate an object in Python?

Copying Arbitrary Python Objects Its copy. copy() and copy. deepcopy() functions can be used to duplicate any object.

Can self be replaced in Python?

self is only a reference to the current instance within the method. You can't change your instance by setting self .

Can you have two methods with the same name in Python?

In python, unlike other languages, you cannot perform method overloading by using the same method name. Why? Everything is an object in python, classes, and even methods. Say you have an object Addition, which is a class (everything in python is an object, so the class Addition is an object too).


2 Answers

Using some tips from:

Is it possible to change an instance's method implementation without changing all other instances of the same class?

you can do the following, by using the types module to assign a method to the object created without affecting the class. You need to do this because a function does not automatically receive the self object as the first variable, but a method does.

import types

joe = Person()
bob = Person()

joe.SayHi()
>>> Hello!

def greedy_has_good_mood(self):
    return self.Cash > 100

joe.HasGoodMood = types.MethodType(greedy_has_good_mood, joe)


joe.SayHi()
>>> Hmpf.
bob.SayHi()

>>> Hello!
like image 100
Andrew McDowell Avatar answered Oct 01 '22 20:10

Andrew McDowell


When you write a def in a class, and then call it on an instance, that's a method, and the mechanics of method-calling will fill in the self argument when you call it.

By assigning to HasGoodMood in your instance, you are not putting a new method there, but putting a function into the attribute. You can read the attribute to get the function, and call it, and though that looks like a method call, it's just calling a function that happens to be stored in an attribute. You won't get the self parameter supplied automatically.

But you already know what self is going to be, since you're assigning this function into one particular object.

greedy_jack.HasGoodMood = (lambda self=greedy_jack: self.Cash > 100)

This associates the function argument self with the current value of the variable greedy_jack.

Lambdas in Python can only be one line. If you needed a longer function, you could use a def instead.

def greedy_jack_HasGoodMood(self=greedy_jack):
    return self.Cash > 100

greedy_jack.HasGoodMood = greedy_jack_HasGoodMood

For a less hacky solution, see Andrew McDowell's answer.

like image 36
khelwood Avatar answered Oct 01 '22 21:10

khelwood