Home > Mobile >  How to decorate a python class and override a method?
How to decorate a python class and override a method?

Time:06-19

I have a class

class A:
     def sample_method():

I would like to decorate class A sample_method() and override the contents of sample_method()

class DecoratedA(A):
    def sample_method():

The setup above resembles inheritance, but I need to keep the preexisting instance of class A when the decorated function is used.

a # preexisting instance of class A
decorated_a = DecoratedA(a)
decorated_a.functionInClassA() #functions in Class A called as usual with preexisting instance
decorated_a.sample_method() #should call the overwritten sample_method() defined in DecoratedA

What is the proper way to go about this?

CodePudding user response:

There isn't a straightforward way to do what you're asking. Generally, after an instance has been created, it's too late to mess with the methods its class defines.

There are two options you have, as far as I see it. Either you create a wrapper or proxy object for your pre-existing instance, or you modify the instance to change its behavior.

A proxy defers most behavior to the object itself, while only adding (or overriding) some limited behavior of its own:

class Proxy:
    def __init__(self, obj):
        self.obj = obj

    def overridden_method(self):     # add your own limited behavior for a few things
        do_stuff()

    def __getattr__(self, name):     # and hand everything else off to the other object
        return getattr(self.obj, name)

__getattr__ isn't perfect here, it can only work for regular methods, not special __dunder__ methods that are often looked up directly in the class itself. If you want your proxy to match all possible behavior, you probably need to add things like __add__ and __getitem__, but that might not be necessary in your specific situation (it depends on what A does).

As for changing the behavior of the existing object, one approach is to write your subclass, and then change the existing object's class to be the subclass. This is a little sketchy, since you won't have ever initialized the object as the new class, but it might work if you're only modifying method behavior.

class ModifiedA(A):
    def overridden_method(self):     # do the override in a normal subclass
        do_stuff()

def modify_obj(obj):                 # then change an existing object's type in place!
    obj.__class__ = ModifiedA        # this is not terribly safe, but it can work

You could also consider adding an instance variable that would shadow the method you want to override, rather than modifying __class__. Writing the function could be a little tricky, since it won't get bound to the object automatically when called (that only happens for functions that are attributes of a class, not attributes of an instance), but you could probably do the binding yourself (with partial or lambda if you need to access self.

CodePudding user response:

First, why not just define it from the beginning, how you want it, instead of decorating it?

Second, why not decorate the method itself?

To answer the question:
You can reassign it

class A:
     def sample_method(): ...
pass
A.sample_method = DecoratedA.sample_method;

but that affects every instance.

Another solution is to reassign the method for just one object.

import functools;
a.sample_method = functools.partial(DecoratedA.sample_method, a);

Another solution is to (temporarily) change the type of an existing object.

a = A();
a.__class__ = DecoratedA;
a.sample_method();
a.__class__ = A;

CodePudding user response:

To create a custom decorator in Python, you must have an outer function that calls a separate function within it. Hopefully this link can help you out! https://github.com/Asabeneh/30-Days-Of-Python/blob/master/14_Day_Higher_order_functions/14_higher_order_functions.md

  • Related