如何重写第三方库使用的方法

时间:2018-09-04 21:48:27

标签: python

这就是布局

some_function.py

def some_function():
    print("some_function")

some_library.py

from some_function import some_function

class A:
    def xxx(self):
        some_function()

main.py

from some_library import A
from some_function import some_function

def new_some_function():
    print("new_some_function")

if __name__ == '__main__':
    some_function = new_some_function
    a = A()
    a.xxx()

在类A中,方法xxx调用了some_function,是否有可能用其他方法覆盖它而无需重新实现整个类?

4 个答案:

答案 0 :(得分:2)

您在这里提供的用例信息很少。正如其中一项评论指出的那样,这可能是继承的情况。如果您处于测试环境中,则可能不希望使用继承,而是希望使用模拟对象。

这是继承版本:

from some_library import A

def new_some_function():
    print("new_some_function")

class B(A):
    def xxx(self):
        new_some_function()

if __name__ == '__main__':
    a = B()
    a.xxx()

请注意,类B是如何通过A语句从类class B(A)派生的。这样,类B继承了A的所有功能,并且类B的定义仅包含BA不同的部分。在您的示例中,事实是xxx方法应调用new_some_function而不是some_function

这是模拟版本:

from unittest import mock
from some_library import A

def new_some_function():
    print("new_some_function")

if __name__ == '__main__':
    with mock.patch('some_library.some_function') as mock_some_function:
        mock_some_function.side_effect = new_some_function
        a = A()
        a.xxx()

如上所述,如果您处于测试环境中并且some_function所做的事情代价昂贵和/或不可预测,则此方法最有用。为了测试涉及到对some_function的调用的代码,您可能会暂时想用其他替换some_function的方法,这种方法调用起来很便宜,并且行为可预测。实际上,对于这种情况,用some_function代替new_some_function甚至可能超出实际需要。也许,您只需要一个可以被调用且总是返回相同值的空壳(而不是side_effect行,您可以在上面的代码示例中指定一个常量.return_value)。模拟对象的关键功能之一是您以后可以检查该函数是否已被调用。如果测试是您的用例,我非常建议您查看python模拟模块的文档。

请注意,该示例使用mock.patch上下文管理器。这意味着在托管上下文中(即with语句中的块)some_library.some_function被模拟对象代替,但是一旦离开托管上下文,原始功能就会恢复原位。

答案 1 :(得分:1)

我认为您正在寻找 monkey patching (意味着在运行时动态更改类/模块)。这样,您就无需覆盖类A并使用其他注释所建议的继​​承-您表示不希望这样做,因此请尝试以下解决方案:

import some_class  # import like this or will not work (cos of namespaces)

def new_some_function():
   print("new_some_function")

if __name__ == '__main__':
    # Import and use like this, other ways of import will not work.
    # Because other way imports method to your namespace and then change it in your namespace,
    # but you need to change it in the original namespace
    some_class.some_function = new_some_function

那样,替换原始方法,然后其他类也将使用它。请注意,如果原始方法是类/实例方法,则需要使用适当的参数创建新函数,如下所示:

def new_some_function(self):
    # for instance methods, you may add other args, but 'self' is important

def new_some_function(cls):
    # for class methods, you may add other args, but 'cls' is important

答案 2 :(得分:0)

您可以只创建另一个类并覆盖所需的方法。
举个例子:

class myInt(int):
    def __pow__(self, x):
        return 0

a = myInt(10)
a+10 # 20
a**2 # 0

在这种情况下,a是一个整数,可以访问int类的所有方法,但是将使用我定义的__pow__方法。

答案 3 :(得分:0)

您需要的是继承,您可以继承一个类,并且可以使用super方法继承所有父类函数。如果要覆盖父类函数,则只需在子类中提供具有相同名称的其他实现。
     从some_library导入A      从some_function导入some_function

 def new_some_function():
     print("new_some_function")

class B(A):
    def __init__(*args, **kwargs):
       super().__init__(self)
       pass
    def xxx(self):
       new_some_function()


if __name__ == '__main__':
    a = B()
    a.xxx()

Output:
     new_some_function

you syntax may differ depending upon the python version.
In python3
class B(A):
   def __init__(self):
       super().__init__() 

In Python 2,
class B(A):
    def __init__(self):
      super(ChildB, self).__init__()