web-dev-qa-db-fra.com

Python: remplacement d'une fonction dans une classe d'un module

J'essaie de remplacer une fonction définie dans une classe afin de modifier sa fonction (comme dans les rouages ​​internes) sans changer le code réel. Je n'ai jamais fait cela auparavant, donc j'ai eu des problèmes lors du remplacement. Changer le code me fera accéder au paquet dans ma bibliothèque python qui n'est pas vraiment une option.

Par exemple, si le module s'appelait testMOD

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

Ensuite, j'importerais testMOD, définirais une classe (mytest = testMOD ()) et accèderais à la fonction définie dans la classe, testFunc, et la changerais en fonction déjà définie.

Par exemple,

from somemodule import testMOD
mytest = testMOD()

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

#problem here
mytest.testFunc = alternativeFunc

Comme vous pouvez le voir, si je remplace manuellement (?) La fonction de la classe par ma fonction définie, elle ne fonctionnera pas correctement. Cela ne donne pas d'erreurs de syntaxe, cependant, le problème est que la fonction remplacée pense que le "soi" est une autre variable pour la fonction et dit qu'elle nécessite un autre argument pour la variable "variable" (je suppose que ce n'était pas un bon nom).

Ce que je veux faire, c'est que la fonction de remplacement soit exactement la même chose que la fonction remplacée, mais avec du code supplémentaire ou quelques modifications mineures. Cependant, le "soi" ne fonctionne pratiquement pas comme il devrait l'être dans une classe. Y aurait-il un moyen d'implémenter correctement une fonction définie pour remplacer une fonction d'une classe importée?

9
Cody Chung

Je propose 4 solutions, du pire au meilleur (à mon humble avis), mais bien sûr cela dépend aussi de vos contraintes spécifiques:

  1. Remplacez la méthode d'instance (1): j'utilise le fait que les fonctions sont des descripteurs en Python, pour pouvoir utiliser le __get__ méthode sur AlternativeFunc pour l'obtenir comme méthode de l'instance mytest et écraser la méthode testFunc de l'instance mytest (sans écraser la méthode de classe ):

    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. Remplacez la méthode d'instance (2): cette fois, j'utilise types.MethodType qui est un peu plus lisible que la première solution:

    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. Effectuez une correction de singe de la méthode de classe. À la différence de la première méthode, elle modifie le comportement de n'importe quelle instance de la classe:

    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. Créez une classe héritée de testMOD pour remplacer la méthode:

    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
    
19
Laurent H.

Vous pouvez patch singe cette méthode comme suit:

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)

production:

original 24
alternative 13.2
9
Reblochon Masque

vérifier l'héritage de classe en python, pour créer votre propre classe personnalisée:

from somemodule import TestMOD

class YourCustomClass(TestMOD):

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

your_class = YourCustomClass()
your_class.test_func(x)
1
ziiiro