2017-04-21 42 views
1

我想学习一些高级修饰器的用法。具体来说,我试图通过一个函数中的装饰器来修补类的方法。python monkey通过修饰器修补函数中的对象的方法

这是一个基本的例子来说明我想要做什么。我有一个功能something,它可以做一些事情;并且在该函数内有一个类的实例。那个例子我想猴子补丁。

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()) 

我想无论是想做一个1比1置换FoobarFakeFoobar或猴补丁Foobarget_something方法FakeFoobarget_something方法。

当我运行上面的代码,我得到如下:

>>> something() 
'apple' 
>>> 

我想找到某种方式增加了Foobarget_something方法,使我们得到如下输出:

>>> something() 
'orange' 
>>> 

unittests库中有一个mock模块,但是,我不清楚如何将其用于我的用例。我很相信,不会将参数传递给装饰器,也不会像something函数那样引入额外的参数,因为很多mock库显示的示例都是如此。

我还注意到moto library正在完成类似于我正在做的事情。我试着深入了解源代码,但对于我正在尝试做的事情来说似乎相当复杂。

回答

0

如何更新函数的全局变量字典?

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 
+0

这非常接近。问题在于它更新了整个模块的全局变量,所以如果我在'something'函数之外重新实例化'Foobar',我会返回'FakeFoobar',这是不可取的。 – user5038859

+0

@ user5038859:您可以随时恢复变量,就像我刚更新的答案一样。 – SuperSaiyan

+0

这就是我一直在寻找的,谢谢。在将此标记为答案之前,请接受我对python3支持的编辑。 – user5038859