The Python Oracle

How to call the original method when it is monkey-patched?

--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------

Take control of your privacy with Proton's trusted, Swiss-based, secure services.
Choose what you need and safeguard your digital life:
Mail: https://go.getproton.me/SH1CU
VPN: https://go.getproton.me/SH1DI
Password Manager: https://go.getproton.me/SH1DJ
Drive: https://go.getproton.me/SH1CT


Music by Eric Matyas
https://www.soundimage.org
Track title: Magic Ocean Looping

--

Chapters
00:00 How To Call The Original Method When It Is Monkey-Patched?
01:40 Answer 1 Score 1
02:02 Answer 2 Score 2
02:11 Answer 3 Score 23
02:36 Accepted Answer Score 6
03:16 Thank you

--

Full question
https://stackoverflow.com/questions/8726...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #oop #python3x #monkeypatching #overriding

#avk47



ANSWER 1

Score 23


This is what we call a 'decorator' pattern. Replace the original reassignment of name to have it call a function instead, which takes the original. It then returns a new function.

def name_decorator(method):
    def decorate_name(self=None):
        return stuff + method(self)
    return decorate_name
A.name = name_decorator(A.name)

Later, calling A.name will call decorate_name with self as the current instance and method will be available to it which points to the function at the time of the reassignment.




ACCEPTED ANSWER

Score 6


Here's a full example of what I was hinting at. Feel free to yell at me and have me merge my answers, or downvote one or whatever, just easier to provide an alternative as a new answer. I'll let the code do the talking instead of poorly explaining it. :)

## Some shared class that is used all over the place and needs to be patched.

class A(object):
    def __init__(self):
        self.firstname = 'Bob'

    # Print my first name.
    def name(self):
        return self.firstname

    # Use this to allow patching arbitrary methods...
    @classmethod
    def patch(cls, func_name):
        def patch_by_name(new_func):
            old_func = getattr(cls, func_name)
            def patched_func(self):
                return new_func(self, old_func)
            setattr(cls, func_name, patched_func)
        return patch_by_name

## Some other area of the code where you want to throw in a patch

class PatchedA(A):  # doesn't need to subclass, but comes in handy sometimes
    @A.patch('name')
    def name(self, orig_func):
        return 'I am ' + orig_func(self) + 'McWizwaz'

print 'Who are you, A class?'
print A().name()  # prints 'I am Bob McWizwaz'



ANSWER 3

Score 2


class ABase(object):
    def name(self):
        pass

class A(object):
    pass

class AExtension(ABase):
    def name(self):
        return ABase.name(self)

A.name = AExtension.name



ANSWER 4

Score 1


One option that might not always be the best in a language like Python is to use the @override decorator from this non-standard package. But this is a viable option only if your two functions work on different types or different number of arguments. Other than that, there's not much you can do, besides renaming your function.