asheets
asheets

Reputation: 860

Override class method while keeping the original class method call

I'd like to modify a class method to do some things in addition to the original method call. I have a toy example posted below.

Example:

class str2(str):
    def __init__(self, val):
        self.val = val

    def upper(self):
        print('conveting to upper')
        return self.upper()

x = str2('a')
print(x.upper())

This does what I should have expected and gives a maximum recursion depth error. Is it possible to modify the upper method so that it prints some text before calling the actual str.upper method, while ideally keeping the name the same?

I've been wondering if this is the situation to use a decorator, but I am not familiar enough with them to have a clear idea on how to do this.

Upvotes: 0

Views: 101

Answers (4)

DocDriven
DocDriven

Reputation: 3974

As @Schalton stated, there is a way to do it without having to inherit from str by using decorators. Consider this snippet:

def add_text(func):
    def wrapper(*args, **kwargs):
        print('converting to upper')
        return func(*args)
    return wrapper

class str2:
    def __init__(self, val):
        self.val = val

    @add_text
    def upper(self):
        return self.val.upper()

instance = str2('a')

print(instance.upper())

The great advantage of this is that the wrapper is reusable, e.g. if you have another class that you want to modify with the exact same behavior, you can just add the @decorator and don't have to redo all the work. Also, removing the additional functionality gets also easier.

Upvotes: 0

Olivier Melançon
Olivier Melançon

Reputation: 22294

In the method str2.upper you are calling str2.upper which in turn calls str2.upper which... You see where this is going.

What you probably intended to so was to call str.upper from str2.upper. This is done by using super. Calling super() returns an object which delegates method calls to the parent classes.

class str2(str):
    def upper(self):
        print('converting to upper')
        return super().upper()

Upvotes: 1

U13-Forward
U13-Forward

Reputation: 71570

The solution would be:

class str2(str):
    def __init__(self, val):
        self.val = val

    def upper(self):
        print('conveting to upper')
        return str.upper(self.val)

x = str2('a')
print(x.upper())

My point to the your code:

  1. in upper function you just print it then go to same function again and again

  2. this makes it that it will keep printing

  3. it raises an error at the end because python basically has enough of this

My point to my code:

  1. use the method-descriptor (<method 'upper' of 'str' objects>) to use it so it doesn't confuse it with self

  2. use that because it will still be calling the real str class (not the metaclass)

I am also blaming myself that why i didn't think of:

class str2(str):
    def __init__(self, val):
        self.val = val

    def upper(self):
        print('conveting to upper')
        return self.val.upper()

x = str2('a')
print(x.upper())

Upvotes: 1

Schalton
Schalton

Reputation: 3104

Research "Mapping" and "decorators" - I think there's an easier/more pythonic way to do what you're trying to do.

Upvotes: 0

Related Questions