Стоя на плечах гигантов (@spencer), вот немного более общий образец.Это позволяет исправлять класс Original, не касаясь его исходного кода.В этой версии параметры разлетаются повсюду, и новому методу дается ссылка на произвольный контекст.
class Original:
def f( self, a, b = None ):
print( "f", a, b )
self.g( b )
def g( self, b ):
print( "g", b )
class Patcher:
def prepare_class( self, clazz ):
@classmethod
def on_class_patcher( cls, func_name, context ):
def patch_by_name( new_func) :
old_func = getattr( cls, func_name )
def patched_func( self, *args, **kwargs ):
return new_func( self, old_func, context, *args, **kwargs )
setattr( cls, func_name, patched_func )
return patch_by_name
setattr( clazz, "patch", on_class_patcher )
class Another:
def log( self, level, info ):
print( level, info )
Теперь давайте что-нибудь исправим:
obj = Original()
obj.f( 1, b = "hello" )
p = Patcher()
p.prepare_class( clazz = Original )
logger = Another()
@Original.patch( "f", context = logger )
def new_f( self, old_f, context, a, b ):
print( "new_f", a, b )
context.log( "zzz", a )
old_f( self, a, b )
obj = Original()
obj.f( 1, b = "hello" )
вывод:
f 1 hello
g hello
new_f 1 hello
zzz 1
f 1 hello
g hello