Il y a deux problèmes de base que vous rencontrez ici:
__xxx__
les méthodes ne sont recherchées que sur la classe
TypeError: can't set attributes of built-in/extension type 'module'
(1) signifie que toute solution devrait également garder une trace du module en cours d'examen, sinon chaque module aurait alors le comportement de substitution d'instance; et (2) signifie que (1) n'est même pas possible ... du moins pas directement.
Heureusement, sys.modules n'est pas pointilleux sur ce qui s'y passe, donc un wrapper fonctionnera, mais uniquement pour l'accès au module (c'est-à-dire import somemodule; somemodule.salutation('world')
; pour l'accès au même module, vous devez à peu près extraire les méthodes de la classe de substitution et les ajouter à globals()
eiher avec un méthode personnalisée sur la classe (j'aime utiliser .export()
) ou avec une fonction générique (comme celles déjà répertoriées comme réponses). Une chose à garder à l'esprit: si le wrapper crée une nouvelle instance à chaque fois, et que la solution globale ne l'est pas, vous vous retrouvez avec un comportement subtilement différent Oh, et vous ne pouvez pas utiliser les deux en même temps - c'est l'un ou l'autre.
Mettre à jour
De Guido van Rossum :
Il y a en fait un hack qui est parfois utilisé et recommandé: un module peut définir une classe avec la fonctionnalité souhaitée, puis à la fin, se remplacer dans sys.modules par une instance de cette classe (ou par la classe, si vous insistez , mais c'est généralement moins utile). Par exemple:
# module foo.py
import sys
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
sys.modules[__name__] = Foo()
Cela fonctionne parce que la machine d'importation active activement ce hack et que sa dernière étape extrait le module réel de sys.modules, après l'avoir chargé. (Ce n'est pas un hasard. Le hack a été proposé il y a longtemps et nous avons décidé que nous aimions suffisamment pour le supporter dans la machinerie d'importation.)
Donc, la façon établie d'accomplir ce que vous voulez est de créer une seule classe dans votre module, et comme dernier acte du module, remplacez-la sys.modules[__name__]
par une instance de votre classe - et maintenant vous pouvez jouer avec __getattr__
/ __setattr__
/ __getattribute__
si nécessaire.
Remarque 1 : Si vous utilisez cette fonctionnalité, tout le reste du module, comme les globaux, les autres fonctions, etc., sera perdu lors de l' sys.modules
affectation - assurez-vous donc que tout ce qui est nécessaire se trouve dans la classe de remplacement.
Note 2 : Pour soutenir, from module import *
vous devez avoir __all__
défini dans la classe; par exemple:
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
__all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})
Selon votre version de Python, il peut y avoir d'autres noms à omettre __all__
. Le set()
peut être omis si la compatibilité Python 2 n'est pas nécessaire.