I have the following code (python 3):
import inspect
def get_class_for_method(method):
if inspect.ismethod(method):
for cls in inspect.getmro(method.__self__.__class__):
if cls.__dict__.get(method.__name__) is method:
return cls
method = method.__func__ # fallback to __qualname__ parsing
if inspect.isfunction(method):
cls = getattr(inspect.getmodule(method),
method.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
if isinstance(cls, type):
return cls
return None
class A:
def __init__(self):
self.name = 'Class A'
def foo(self):
print('foo: ' + self.name)
def bar(self):
print('bar: ' + self.name)
setattr(A, bar.__name__, bar)
instance_of_a = A()
instance_of_a.foo()
instance_of_a.bar()
print(get_class_for_method(A.foo))
print(get_class_for_method(A.bar))
And get the following output:
foo: Class A
bar: Class A
<class '__main__.A'>
None
So the methods are working like expected. The only thing what I'm not able to do is to bind the method to the object. So that print(get_class_for_method(A.bar))
will return <class '__main__.A'>
instead of None
.
types.MethodType
of the types module looks like exactly what I need but it works only on an explicit instances of the class. What I figured out so far is that I have to set the __self__
attribute of the method, but if I'm trying that seems not to change anything.
Edit: Additional information.
The idea behind my question is to write a decorator method to overwrite (monkey patch) class methods. The following code is the one I have for now. It works for overwriting methods like expected, but if a method was added dynamically, it fails because get_class_for_method
(grabbed from Get defining class of unbound method object in Python 3) return None
.
def patch_method(original_fnc):
def get_class_for_method(method):
if inspect.ismethod(method):
for cls in inspect.getmro(method.__self__.__class__):
if cls.__dict__.get(method.__name__) is method:
return cls
method = method.__func__ # fallback to __qualname__ parsing
if inspect.isfunction(method):
cls = getattr(inspect.getmodule(method),
method.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
if isinstance(cls, type):
return cls
return None
def wrap(fnc):
fnc_object = get_class_for_method(original_fnc)
if fnc_object is not None:
if fnc.__doc__ is None:
fnc.__doc__ = original_fnc.__doc__
method = partial(fnc, original_fnc)
method.__doc__ = fnc.__doc__
setattr(fnc_object, original_fnc.__name__, method)
return None
return wrap
Usage of the decorator:
@patch_method(module.Object.method)
def patched_method(original, self, arg):
print('foo')
original(self, arg)
print('bar')