【问题标题】:Python: replacing a function within a class of a modulePython:替换模块类中的函数
【发布时间】: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'变量的另一个参数(我猜不是个好名字)。

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

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

【问题讨论】:

    标签: python function class oop python-module


    【解决方案1】:

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

    1. 替换实例方法(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. 替换实例方法(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 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 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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-06-02
          • 1970-01-01
          • 2011-06-24
          • 2011-02-13
          • 2015-03-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多