Skip to content

Instantly share code, notes, and snippets.

@nmfzone
Last active February 4, 2020 03:43
Show Gist options
  • Save nmfzone/9345fe26bb43db65bd5c2d966c1c86d2 to your computer and use it in GitHub Desktop.
Save nmfzone/9345fe26bb43db65bd5c2d966c1c86d2 to your computer and use it in GitHub Desktop.
Monkey Patch: Override method without extending class in Python (e.g. Patch method, Override core method)
def override_method(cls, after=True):
def decorator(func):
new_parent_method_name = '_' + func.__name__ + '_parent'
setattr(cls, new_parent_method_name, getattr(cls, func.__name__))
@wraps(func)
def wrapper(self, *args, **kwargs):
if after:
parent_result = getattr(self, new_parent_method_name)(*args, **kwargs)
return func(self, parent_result, *args, **kwargs)
elif after == False:
func(self, *args, **kwargs)
return getattr(self, new_parent_method_name)(*args, **kwargs)
return func(self, *args, **kwargs)
setattr(cls, func.__name__, wrapper)
return func
return decorator
@nmfzone
Copy link
Author

nmfzone commented Jan 7, 2020

It's fine (actually the correct way) when you only need to override method in that class.

But, what about when the BaseClass is a core class? Then it's gets used everywhere, and has other core class that override it?

This is useful when you only want to modify / override the method in the root class.

@override_method(BaseClass)  # just like ClassOne does
def foo(self, parent_result, x, y):  # you had to define `parent_result` argument, it's the parent result return value.
    return parent_result * 2

@override_method(BaseClass, False)  # just like ClassTwo does
def foo(self, x, y):  # you should not define `parent_result` argument, it's just used when `after=True`
    x = x - 2  # this is just for example. It will not working as expected, since it's local variable.

@override_method(BaseClass, None)  # just like ClassThree does
def foo(self, x, y):  # you should not define `parent_result` argument, it's just used when `after=True`
    return ((x - 2) + y) * 2

You just need to place that code somewhere, the place that your app always access it no matter what, for example in __init__.py file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment