eto3542
eto3542

Reputation: 91

How to overwrite a method of a dynamically created class instance

A class I cannot change the code of has a method from_lowercase that returns an instance of itself. Now I would like to inherit a class. However, when calling the inherited class over from_lowercase an instance of the original class is returned.

class Animal:
    """I CANNOT change the code in this class"""
    def __init__(self, uppercase_name: str):
        self.uppercase_name = uppercase_name

    def say(self) -> str:
        return f"I am {self.uppercase_name}"

    @staticmethod
    def from_lowercase(lowercase_name: str) -> "Animal":
        return Animal(lowercase_name.upper())


class PoliteAnimal(Animal):
    def say(self) -> str:
        return f"Hello dear sir, {super().say()}!"
    # insert magic here

calling some examples:

animal = Animal("JOHN")
animal_from_lowercase = Animal.from_lowercase("john")
nice_animal = PoliteAnimal("MARIA")
nice_animal_from_lowercase = PoliteAnimal.from_lowercase("maria")

print(animal.say())
print(animal_from_lowercase.say())
print(nice_animal.say())
print(nice_animal_from_lowercase.say())

leads to the following:

I am JOHN
I am JOHN
Hello dear sir, I am MARIA!
I am MARIA

However, the desired output is the following:

I am JOHN
I am JOHN
Hello dear sir, I am MARIA!
Hello dear sir, I am MARIA!

Obviously nice_animal_from_lowercase is an instance of Animal and not PoliteAnimal. How can this be fixed?

Upvotes: 2

Views: 66

Answers (2)

chepner
chepner

Reputation: 531055

I would patch Animal to fix from_lowercase, which should be a class method, not a static method.

def from_lowercase(cls, lc: str):
    return cls(lc.upper())

Animal.from_lowercase = classmethod(from_lowercase)

This works even if you do it after PoliteAnimal is defined.

When you call PoliteAnimal.from_lowercase(...), the method lookup will result in a call to Animal.from_lowercase, but as a class method it will receive PoliteAnimal as its first argument, leading to the correct class being instantiated.

Upvotes: 2

RootOnChair
RootOnChair

Reputation: 137

You need to implement the same method on your subclass so it will be overridden

class PoliteAnimal(Animal):
    def say(self) -> str:
        return f"Hello dear sir, {super().say()}!"
    # insert magic here
    @staticmethod
    def from_lowercase(lowercase_name: str) -> "Animal":
        return PoliteAnimal(lowercase_name.upper())

Ouput:

I am JOHN
I am JOHN
Hello dear sir, I am MARIA!
Hello dear sir, I am MARIA!

Upvotes: 2

Related Questions