python 猴子通过装饰器在函数内修补对象的方法
python monkey patching an object's method within a function via a decorator
我正在尝试学习一些高级装饰器用法。具体来说,我试图通过函数内的装饰器来猴子修补 class 的方法。
这是一个基本示例,用于说明我正在尝试做的事情。我有一个函数 something
可以做一些事情;在那个函数中有一个 class 的实例。那个例子我想猴子补丁。
from functools import update_wrapper
class Foobar:
def get_something(self):
return "apple"
class FakeFoobar:
def get_something(self):
return "orange"
class my_decorator:
def __init__(self, original_function):
self._original_function = original_function
update_wrapper(self, original_function)
def __call__(self, *args, **kwargs):
# some magic here?
return self._original_function(*args, **kwargs)
@my_decorator
def something():
f = Foobar()
return f.get_something()
if __name__ == '__main__':
print(something())
我正在尝试用 Foobar
到 FakeFoobar
进行 1 对 1 替换,或者,猴子补丁 Foobar
的 get_something
方法到 FakeFoobar
的get_something
方法。
当我运行上面的代码时,我得到以下信息:
>>> something()
'apple'
>>>
我想找到一些方法来增强 Foobar
的 get_something
方法,以便我们得到以下输出:
>>> something()
'orange'
>>>
unittests
库中有一个 mock
模块,但是,我不清楚如何将其用于我的用例。正如 mock 库中的许多示例所示,我非常赞同不将参数传递给装饰器或将额外参数传递给 something
函数的想法。
我还注意到 moto library 正在完成与我正在尝试做的事情类似的事情。我尝试深入研究源代码,但对于我想要做的事情来说似乎相当复杂。
如何更新函数的全局变量dict?
from functools import update_wrapper
class Foobar:
def get_something(self):
return "apple"
class FakeFoobar:
def get_something(self):
return "orange"
class my_decorator:
def __init__(self, original_function):
self._original_function = original_function
update_wrapper(self, original_function)
def __call__(self, *args, **kwargs):
f = self._original_function
restore_val = f.func_globals['Foobar']
f.func_globals['Foobar'] = f.func_globals['FakeFoobar']
# ^^^^^ This is your magic-line.
try:
return f(*args, **kwargs)
except:
raise
finally:
f.func_globals['Foobar'] = restore_val
@my_decorator
def something():
f = Foobar()
return f.get_something()
if __name__ == '__main__':
print(something()) #Prints orange
print(Foobar().get_something()) #Prints apple
我正在尝试学习一些高级装饰器用法。具体来说,我试图通过函数内的装饰器来猴子修补 class 的方法。
这是一个基本示例,用于说明我正在尝试做的事情。我有一个函数 something
可以做一些事情;在那个函数中有一个 class 的实例。那个例子我想猴子补丁。
from functools import update_wrapper
class Foobar:
def get_something(self):
return "apple"
class FakeFoobar:
def get_something(self):
return "orange"
class my_decorator:
def __init__(self, original_function):
self._original_function = original_function
update_wrapper(self, original_function)
def __call__(self, *args, **kwargs):
# some magic here?
return self._original_function(*args, **kwargs)
@my_decorator
def something():
f = Foobar()
return f.get_something()
if __name__ == '__main__':
print(something())
我正在尝试用 Foobar
到 FakeFoobar
进行 1 对 1 替换,或者,猴子补丁 Foobar
的 get_something
方法到 FakeFoobar
的get_something
方法。
当我运行上面的代码时,我得到以下信息:
>>> something()
'apple'
>>>
我想找到一些方法来增强 Foobar
的 get_something
方法,以便我们得到以下输出:
>>> something()
'orange'
>>>
unittests
库中有一个 mock
模块,但是,我不清楚如何将其用于我的用例。正如 mock 库中的许多示例所示,我非常赞同不将参数传递给装饰器或将额外参数传递给 something
函数的想法。
我还注意到 moto library 正在完成与我正在尝试做的事情类似的事情。我尝试深入研究源代码,但对于我想要做的事情来说似乎相当复杂。
如何更新函数的全局变量dict?
from functools import update_wrapper
class Foobar:
def get_something(self):
return "apple"
class FakeFoobar:
def get_something(self):
return "orange"
class my_decorator:
def __init__(self, original_function):
self._original_function = original_function
update_wrapper(self, original_function)
def __call__(self, *args, **kwargs):
f = self._original_function
restore_val = f.func_globals['Foobar']
f.func_globals['Foobar'] = f.func_globals['FakeFoobar']
# ^^^^^ This is your magic-line.
try:
return f(*args, **kwargs)
except:
raise
finally:
f.func_globals['Foobar'] = restore_val
@my_decorator
def something():
f = Foobar()
return f.get_something()
if __name__ == '__main__':
print(something()) #Prints orange
print(Foobar().get_something()) #Prints apple