web-dev-qa-db-fra.com

Monkey-patch Python

J'ai une classe, située dans un module séparé, que je ne peux pas changer.

from module import MyClass

class ReplaceClass(object)
  ...

MyClass = ReplaceClass

Cela ne change pas MyClass ailleurs que dans ce fichier. Cependant, si j'ajoute une méthode comme celle-ci

def bar():
   print 123

MyClass.foo = bar

cela fonctionnera et la méthode foo sera disponible partout ailleurs.

Comment remplacer complètement la classe?

44
ZooKeeper
import module
class ReplaceClass(object):
    ....
module.MyClass = ReplaceClass
57
Glenn Maynard

Évitez le from ... import (horrible ;-) moyen d'obtenir des noms de barres lorsque vous avez le plus souvent besoin de qualifiés noms. Une fois que vous avez fait les choses de la bonne façon Pythonique:

import module

class ReplaceClass(object): ...

module.MyClass = ReplaceClass

De cette façon, vous monkeypatch l'objet module, qui est ce dont vous avez besoin et fonctionnera lorsque ce module est utilisé pour d'autres. Avec le from ... forme, vous n'avez tout simplement pas l'objet module (une façon de voir le défaut flagrant de l'utilisation de from ...) et donc vous êtes évidemment moins bien lotis ;-);

La seule façon dont je recommande d'utiliser l'instruction from est d'importer un module à partir d'un package:

from some.package.here import amodule

vous obtenez donc toujours l'objet module et utiliserez des noms qualifiés pour tous les noms de ce module.

29
Alex Martelli

Je ne suis qu'un œuf. . . . C'est peut-être évident pour les non-débutants, mais j'avais besoin du from some.package.module import module idiome.

J'ai dû modifier une méthode de GeneralHelpfulClass. Cela a échoué:

import some.package.module

class SpeciallyHelpfulClass(some.package.module.GenerallyHelpfulClass): 
    def general_method(self):...

some.package.module.GenerallyHelpfulClass = SpeciallyHelpfulClass

Le code a fonctionné, mais n'a pas utilisé les comportements surchargés sur SpeciallyHelpfulClass.

Cela a fonctionné:

from some.package import module

class SpeciallyHelpfulClass(module.GenerallyHelpfulClass): 
    def general_method(self):...

module.GenerallyHelpfulClass = SpeciallyHelpfulClass

Je suppose que le from ... import idiome 'obtient le module', comme l'a écrit Alex, car il sera repris par les autres modules du package. En spéculant davantage, la référence en pointillés plus longue semble amener le module dans l'espace de noms avec l'importation par référence en pointillés longs, mais ne change pas le module utilisé par les autres espaces de noms. Ainsi, les modifications apportées au module d'importation n'apparaîtraient que dans l'espace de nom où elles ont été apportées. C'est comme s'il y avait deux exemplaires du même module, chacun disponible sous des références légèrement différentes.

12
chernevik
import some_module_name

class MyClass(object): 
     ... #copy/paste source class and update/add your logic

some_module_name.MyClass = MyClass

Il est préférable de ne pas changer le nom de la classe lors du remplacement, car quelqu'un peut les avoir référencés à l'aide de getattr - ce qui entraînera un échec comme ci-dessous

getattr(some_module_name, 'MyClass') -> qui échouera si vous avez remplacé MyClass par ReplaceClass!

0
shahjapan