Monkey 从库中修补 Python 类方法

Jun*_*une 5 python monkeypatching

我正在导入其他人制作的库,并且想要更改该库中特定类方法的工作方式,因此我已将该类方法复制到我自己的文件中,并希望在运行时替换它。这对于函数来说似乎工作得很好,但对于类方法来说似乎就不行了。

a.library.package.library_file.py

class LibraryClass(ParentClass):
    @classmethod
    def get_cost(cls, time):
        return int(time * cls.hourly)
Run Code Online (Sandbox Code Playgroud)

我想用这个替换它

class LibraryClass(ParentClass):
    @classmethod
    def get_cost(cls, time):
        return 1234
Run Code Online (Sandbox Code Playgroud)

我尝试只进行正常的替换,这对于常规功能来说效果很好

import a.library.package.library_file

...

a.library.package.library_file.LibraryClass.get_cost = get_cost
Run Code Online (Sandbox Code Playgroud)

但它似乎根本无法正常工作,该方法在错误的时间使用错误的参数调用并导致崩溃。在对 Google、StackOverflow 和 Python 进行一些研究之后,我开始尝试使用模拟类。

from unittest.mock import patch

@patch.object('a.library.package.library_file.LibraryClass', 'get_cost')
def get_cost(cls, time):
    return 1234
Run Code Online (Sandbox Code Playgroud)

好消息是它不会崩溃,坏消息是它没有做任何事情,旧代码仍然存在,就像我的代码不存在一样。

我尝试过各种其他方法来做到这一点,例如

import a.library.package.library_file

@patch.object(a.library.package.library_file.LibraryClass, 'get_cost')
...
Run Code Online (Sandbox Code Playgroud)

或者

from a.library.package.library_file import LibraryClass

@patch.object(LibraryClass, 'get_cost')
...
Run Code Online (Sandbox Code Playgroud)

但每次方法都没有碰过。就像我的代码不存在并且使用旧代码一样。

Jun*_*une 10

事实证明,解决方案就像我想象的那样简单。经过一天多的挖掘,网上没有任何帮助,但我在谷歌的第 2 页上发现了一篇来自中国的晦涩的随机帖子,它终于回答了我的问题。链接在这里

这是有效的结果代码

from types import MethodType
from a.library.package.library_file.py import LibraryClass

def get_cost(cls, time):
    return 1234


LibraryClass.get_cost = MethodType(get_cost, LibraryClass)
Run Code Online (Sandbox Code Playgroud)

这与替换函数相同,只是您需要将其包装在“MethodType”中,因为您换出的代码与该类有“classmethod”联系,因此新代码需要相同的联系才能工作。

  • 我认为它更简单一点,只是`LibraryClass.get_cost = classmethod(get_cost)`。 (4认同)

Mat*_*let 0

您可以导入原始类并创建自己的继承原始类的类吗?然后,您重新定义要重写的方法,并仅使用自定义类而不是原始类:

from a_library import LibraryClass

class YourClass(LibraryClass):
    @classmethod
    def get_cost(cls, time):
        # redefine the method here
        return 1234

cost = YourClass.get_cost(time)
Run Code Online (Sandbox Code Playgroud)

然后只需使用YourClass而不是库类。它的行为与库类完全相同,但使用您的方法而不是原始方法。