Monkey-Patch Python Class

Monkey-patch Python class

import module
class ReplaceClass(object):
....
module.MyClass = ReplaceClass

Monkey patching a class in another module in Python

The following should work:

import thirdpartymodule_a
import thirdpartymodule_b

def new_init(self):
self.a = 43

thirdpartymodule_a.SomeClass.__init__ = new_init

thirdpartymodule_b.dosomething()

If you want the new init to call the old init replace the new_init() definition with the following:

old_init = thirdpartymodule_a.SomeClass.__init__
def new_init(self, *k, **kw):
old_init(self, *k, **kw)
self.a = 43

Monkeypatching a Python class

You can monkey-patch methods onto a class, but it’s done like this:

B.stt_m = staticmethod(lambda x: say('stt_m', x))
B.cls_m = classmethod(lambda x: say('cls_m', x))
B.obj_m = lambda x: say('obj_m', x)

Your version for B.cls_m is OK, but your B.stt_m creates a normal method, and your B.obj_m attaches an instance method to a newly created B(), but then that B() is thrown away, and you test a new B() without the extra method.

There’s usually no need to use types.MethodType in Python:

types.MethodType(function, object_)

is equivalent to

function.__get__(object_)

which is a bit better, although also very rare.

Also (irrelevant but too neat not to mention), in newish versions of Python, your

print('*', msg, 'x =', x, 'type(x) =', type(x))

can just be written as

print(f"* {msg} {x = } {type(x) = }")

Monkeypatch method of instance from class with __slots __

>>> class X():
... __slots__ = ("a",)
... def func():
... print("1")
...
>>> x = X()
>>> type(x)
<class '__main__.X'>
>>> type(x).func = lambda self: print("2")
>>> x.func
<bound method <lambda> of <__main__.X object at 0x7f27fb88b050>>
>>> x.func()
2
>>>

Explanation: you have to monkeypatch the Runtime class instead of the instance.

Monkey-patching class with inherited classes in Python

You need to explicitly overwrite the tuple of base classes in a.AA, though I don't recommend modifying classes like this.

>>> import a
>>> class B:
... def foo(self):
... print(2)
...
>>> a.AA.__bases__ = (B,)
>>> a.AA().foo()
2

This will also be reflected in a.A.__subclasses__() (although I am not entirely sure as to how that works; the fact that it is a method suggests that it computes this somehow at runtime, rather than simply returning a value that was modified by the original definition of AA).

It appears that the bases classes in a class statement are simply remembered, rather than used, until some operation needs them (e.g. during attribute lookup). There may be some other subtle corner cases that aren't handled as smoothly: caveat programmator.

How does one use pytest monkeypatch to patch a class

tested this, works for me:

def test_thing(monkeypatch):
def patched_g(self, value):
return value * 2

monkeypatch.setattr(A, 'g', patched_g)
b = B()
assert b.f(2) == 4

python monkey patch a new class and import it

This is because of the import system.

Reading through the doc, you can find this paragraph:

[...] the statement from spam.ham import eggs, sausage as saus results in

_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0)
eggs = _temp.eggs
saus = _temp.sausage

The problem is: what does __import__() does?

The import statement combines two operations; it searches for the named module, then it binds the results of that search to a name in the local scope. [...]

A direct call to __import__() performs only the module search and, if found, the module creation operation.

So, when you re-import the module, your customization will be lost.

To ensure it stays on, you can import numpy as np and then, when using np - after you assing this new class - you can always access wrong_add.

>>> import numpy as np
>>> np.random.wrong_functions = wrong_functions
>>> np.random.wrong_function.wrong_add(1, 1)
3

EDIT: If you need/want to just call wrong_add instead of full package path to function, you can always assign it to a variable.

>>> wrong_add = np.random.wrong_function.wrong_add
>>> wrong_add(2, 2)
5

Can you monkey-patch built in classes and if not, how do I overload an operator to define addition for two different classes?

As for your first question: it's essentially impossible to alter built-in classes. Although you can mess with other classes that is generally a terrible idea. Instead, you can make a subclass that has the property that you want.

For example:

class mystr(str):
def __add__(self, other):
return mystr(str(self) + str(other))

This code inherits all properties from the str class, except for the one we want to change, namely its addition behavior. By casting self to str we still delegate to str's addition, but we also cast the other argument to str to get the behavior you described.

Finally, we cast back to mystr so that we don't end up with a str.

Now we can do the following:

>>> some_string = mystr("abc")
>>> some_string + 4
"abc4"


Related Topics



Leave a reply



Submit