Python:替换模块类中的函数

Posted

技术标签:

【中文标题】Python:替换模块类中的函数【英文标题】:Python: replacing a function within a class of a module 【发布时间】:2018-11-08 23:25:37 【问题描述】:

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

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

例如,如果模块被称为 testMOD,

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

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

例如,

from somemodule import testMOD
mytest = testMOD()

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

# A problem here
mytest.testFunc = alternativeFunc

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

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

我想让替换函数与被替换函数完全相同,但需要额外的代码或一些小的修改。然而,“自我”几乎没有像在课堂上那样工作。

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

【问题讨论】:

【参考方案1】:

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

    替换实例方法(1):我利用函数是Python中的描述符这一事实,这样我就可以使用AlternativeFunc上的__get__方法来获取它作为实例mytest的方法并覆盖实例mytesttestFunc方法(不覆盖类方法):

    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
    

    对类方法执行猴子修补。与第一种方法不同,它改变了类的任何实例的行为:

    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
    

    创建一个继承自testMOD的类来覆盖方法:

    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
    

【讨论】:

当类是内部类,即对象只在内部实例化到某个外部模块时,我们应该怎么做?现在我能看到的唯一选择是修改源代码并维护我自己的 fork。 方法3是否只适用于某些版本的Python?我在 3.6.8 中尝试过,它没有像你那样更新现有实例。【参考方案2】:

你可以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

【讨论】:

感谢您的回答!当我以为我所做的与你所做的完全一样时,我真的很困惑,然后我意识到我必须更改实际的类,而不是继承的类变量。 值得一提:不需要创建新的TestMOD实例。 Monkey 修补会更改类本身,这也会影响任何现有实例。调用 test_original.testFunc(12) 也会产生相同的效果。 @EdwardFalk 猴子补丁也改变了类描述。例如,更新是否会显示为 ??TestMod ?【参考方案3】:

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

from somemodule import TestMOD

class YourCustomClass(TestMOD):

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

your_class = YourCustomClass()
your_class.test_func(x)

【讨论】:

【参考方案4】:

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

我绝对同意创建一个从 testMod 继承的类是最好的选择,我只是建议从 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 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

【讨论】:

【参考方案5】:

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

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

【讨论】:

以上是关于Python:替换模块类中的函数的主要内容,如果未能解决你的问题,请参考以下文章

python 类中的变量传递给类中的函数

python 类中的函数

Python 类中的变量范围

Python 类中的变量范围

python re模块替换文本文件中的二进制数据?

类中的 Python 主函数