Python:替换模块 class 中的函数

Python: replacing a function within a class of a module

我正在尝试替换 class 中定义的函数,以便在不更改实际代码的情况下修改其功能(如在内部工作中)。 我以前从未这样做过,因此在替换它时遇到了一些问题。

更改代码将使我能够访问我的 Python 库中的包,这不是一个很好的选择。

例如,如果模块名为 testMOD,

class testMOD(object):
    def testFunc(self, variable):
        var = variable
        self.something = var + 12

然后我将导入testMOD,定义一个class (mytest = testMOD()),并访问class中定义的函数,testFunc,并将其更改为已经定义的函数。

例如,

from somemodule import testMOD
mytest = testMOD()

def alternativeFunc(self, variable):
    var = variable
    self.something = var + 1.2

# A problem here
mytest.testFunc = alternativeFunc

如您所见,如果我只是用我定义的函数手动覆盖(?)class 中的函数,它将无法正常工作。

它没有给出任何语法错误,但是,问题是被替换的函数认为 'self' 是该函数的另一个变量,并说它需要 [=33= 的另一个参数] 变量(我想这不是一个好名字)。

我想让替换函数与被替换函数完全一样,但要有额外的代码或一些小的修改。但是,'self' 几乎无法正常工作,因为它应该在 class.

有没有办法正确实现定义的函数来替换导入的函数class?

在 Python 中检查 class 继承以创建您自己的自定义 class:

from somemodule import TestMOD

class YourCustomClass(TestMOD):

    # change the function
    def test_func(self, variable):
        #
        #

your_class = YourCustomClass()
your_class.test_func(x)

你可以monkey patch这个方法如下:

class TestMOD(object):

    def testFunc(self, variable):
        var = variable
        self.something = var + 12
        print(f'original {self.something}')


def alternativeFunc(self, variable):
    var = variable
    self.something = var + 1.2
    print(f'alternative {self.something}')


if __name__ == '__main__':

    test_original = TestMOD()
    test_original.testFunc(12)

    TestMOD.testFunc = alternativeFunc

    test_alternate = TestMOD()
    test_alternate.testFunc(12)

输出:

original 24
alternative 13.2

我建议 4 个解决方案,从最差到最好(恕我直言),当然这也取决于您的具体限制:

  1. 替换实例方法(一):我在Python中利用函数是描述符,这样就可以在[=15=上使用__get__方法] 将其作为实例 mytest 的方法获取并覆盖实例 mytesttestFunc 方法(不覆盖 class 方法):

    class testMOD(object):
        def testFunc(self, variable):
            var = variable
            self.something = var + 12
            print('Original:', self.something)
    
    def alternativeFunc1(self, variable):
        var = variable
        self.something = var + 1.2
        print('Alternative1:', self.something)
    
    mytest1 = testMOD()
    mytest1.testFunc(10)   # Original: 22
    
    mytest1.testFunc = alternativeFunc1.__get__(mytest1, testMOD)
    mytest1.testFunc(10)   # Alternative1: 11.2
    mytestX = testMOD()
    mytestX.testFunc(10)   # Original: 22
    
  2. 替换实例方法(二):这次我用的是types.MethodType,比第一种方案:

    可读性更好一点
    import types
    
    class testMOD(object):
        def testFunc(self, variable):
            var = variable
            self.something = var + 12
            print('Original:', self.something)
    
    def alternativeFunc1(self, variable):
        var = variable
        self.something = var + 1.2
        print('Alternative1:', self.something)
    
    mytest1 = testMOD()
    mytest1.testFunc(10)   # Original: 22
    
    funcType = types.MethodType
    mytest1.testFunc = funcType(alternativeFunc1, mytest1)
    mytest1.testFunc(10)   # Alternative1: 11.2
    mytestX = testMOD()
    mytestX.testFunc(10)   # Original: 22
    
  3. 对class方法进行猴子修补。与第一种方法不同,它改变了 class 的任何实例的行为:

    class testMOD(object):
        def testFunc(self, variable):
            var = variable
            self.something = var + 12
            print('Original:', self.something)
    
    def alternativeFunc2(self, variable):
        var = variable
        self.something = var + 1.2
        print('Alternative2:', self.something)
    
    mytest2 = testMOD()
    mytest2.testFunc(10)   # Original: 22
    
    testMOD.testFunc = alternativeFunc2
    mytest2.testFunc(10)   # Alternative2: 11.2
    mytestX = testMOD()
    mytestX.testFunc(10)   # Alternative2: 11.2
    
  4. 创建一个继承自testMOD的class来覆盖方法:

    class testMODNew(testMOD):
         def testFunc(self, variable):
             var = variable
             self.something = var + 1.2
             print('Alternative3:', self.something)
    
    mytest3 = testMODNew()
    mytest3.testFunc(10) # Alternative3: 11.2
    

这是一种 hack,但您可以使用 lambda 函数:

mytest.testFunc = lambda *args, **kwargs: alternativeFunc(mytest, *args, **kwargs)

由于最初的问题要求一种方法来调用父函数 class 然后再做一些额外的事情,我想我要指出的是,简单地替换函数可能会有问题;如果父 class 以任何方式被修改(它所属的模块被更新),那么您可能必须相应地修改代码。此外,他们可能不想为了在末尾添加一点而重新创建原始函数。

我绝对同意创建一个从 testMod 继承的 class 是最好的选择,我只是建议从 testMod 调用函数然后修改结果。

class testMOD(object):
    def testFunc(self, variable):
        var = variable
        return var + 12
        
class testMODNew(testMOD):
     def testFunc(self, variable):
         return testMOD.testFunc(self,variable) - 10.8
         
mytest4 = testMODNew()
print('Alternative4:', mytest4.testFunc(10)) # Alternative4: 11.2
         

可以进行其他更改,例如,如果您希望具有 class 的对象跟踪调用该方法的次数:

class testMODNew(testMOD):
     __testFuncCount__ = 0
     def testFunc(self, variable):
         self.__testFuncCount__ += 1
         return testMOD.testFunc(self,variable)
         
     def getTestFuncCount(self):
         return self.__testFuncCount__

mytest5 = testMODNew()
print('Original:',mytest5.testFunc(10)) #Original: 10
print('Original:',mytest5.testFunc(10)) #Original: 10
print('testFunc was called', mytest5.getTestFuncCount(), 'times.') 
#testFunc was called 2 times