Hay dos problemas básicos con los que se encuentra aquí:
__xxx__
los métodos solo se buscan en la clase
TypeError: can't set attributes of built-in/extension type 'module'
(1) significa que cualquier solución también tendría que hacer un seguimiento de qué módulo se estaba examinando, de lo contrario, cada módulo tendría el comportamiento de sustitución de instancia; y (2) significa que (1) ni siquiera es posible ... al menos no directamente.
Afortunadamente, sys.modules no es exigente con lo que va allí, por lo que un contenedor funcionará, pero solo para el acceso al módulo (es decir import somemodule; somemodule.salutation('world')
; para el acceso al mismo módulo, prácticamente tiene que extraer los métodos de la clase de sustitución y agregarlos a cada uno globals()
con un método personalizado en la clase (me gusta usar .export()
) o con una función genérica (como las que ya figuran como respuestas). Una cosa a tener en cuenta: si el contenedor está creando una nueva instancia cada vez, y la solución global no lo es, terminas con un comportamiento sutilmente diferente Oh, y no puedes usar ambos al mismo tiempo, es uno o el otro.
Actualizar
De Guido van Rossum :
En realidad, hay un truco que se usa y recomienda ocasionalmente: un módulo puede definir una clase con la funcionalidad deseada y, al final, reemplazarse en sys.modules con una instancia de esa clase (o con la clase, si insiste , pero eso generalmente es menos útil). P.ej:
# module foo.py
import sys
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
sys.modules[__name__] = Foo()
Esto funciona porque la maquinaria de importación está activando activamente este truco y, como paso final, extrae el módulo real de sys.modules, después de cargarlo. (Esto no es accidental. El hack fue propuesto hace mucho tiempo y decidimos que nos gustó lo suficiente como para apoyarlo en la maquinaria de importación).
Entonces, la forma establecida de lograr lo que desea es crear una sola clase en su módulo, y como el último acto del módulo, reemplace sys.modules[__name__]
con una instancia de su clase, y ahora puede jugar con __getattr__
/ __setattr__
/ __getattribute__
según sea necesario.
Nota 1 : Si usa esta funcionalidad, cualquier otra cosa en el módulo, como globales, otras funciones, etc., se perderá cuando sys.modules
se realice la asignación, así que asegúrese de que todo lo necesario esté dentro de la clase de reemplazo.
Nota 2 : Para apoyar from module import *
debe haber __all__
definido en la clase; por ejemplo:
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
__all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})
Dependiendo de su versión de Python, puede haber otros nombres para omitir __all__
. Se set()
puede omitir si no se necesita compatibilidad con Python 2.