Better monkey patch boilerplate?

Hi there,

I have a class A that is part of a 3rd party library and I want to monkey patch A.__init__. But I also want to use (like override) the original one:

class A:
    def __init__(self, *args, **kwargs):
        """Initialize A."""
        print(self)
        ...

My actual code is like this:

from functools import wraps

@wraps(A.__init__)
def __init__new(self, *args, **kwargs):
    __init__org(self, *args, **kwargs)
    ...
    print("Patched")
  
if A.__init__ != __init__new:
    __init__org = A.__init__
    A.__init__ = __init__new
    del __init__new
    ## del __init__org # Don't delete this for later reference.

It works well but reads a little hard. In addition, the temporary variable __init__org can not be deleted as it is referenced in the new function. Is there a better (shorter, cleaner, more readable) way to write this?

I think subclassing is the easiest way to do this:

class A:
    def __init__(self, foo):
        self.foo = foo

class B(A):
    def __init__(self, foo, bar):
        super().__init__(foo)  # Call __init__ of parent class
        self.bar = bar

Then you can use B instead of A.

If you actually need monkey patch the class, I would still go with subclassing and instead monkey patch the module it’s from:

import m
m._A = m.A  # create a copy of the old class just in case

class ANew(m._A):
    def __init__(self, foo, bar):
        super().__init__(foo)
        self.bar = bar

m.A = ANew
1 Like

Thanks for the cool solution!
I didn’t notice that super can be used for the latter case.

In this case, it doesn’t seem necessary to make a copy. I just can write:

class ANew(m.A):
    def __init__(self, foo, bar):
        super().__init__(foo, bar)
        self.bar = bar
m.A = ANew

It works as well.
Thank you for your help!

Yeah the copy is only necessary if you want to access the base class later I think.

Oh, I just overlooked your comment saying “… just in case” :sweat_smile:

1 Like