¿Interceptando llamadas al módulo?

Estoy intentando “interceptar” todas las llamadas a un módulo específico y redireccionarlas a otro objeto. Me gustaría hacer esto para poder tener una architecture de plugin bastante simple.

Por ejemplo, en main.py

import renderer renderer.draw('circle') 

En renderer.py

 specificRenderer = OpenGLRenderer() #Then, i'd like to route all calls from main.py so that #specificRenderer.methodName(methodArgs) is called # ie the above example would call specificRenderer.draw('circle') 

Esto significa que cualquier función solo puede importar el renderizador y usarlo, sin preocuparse por los detalles. También significa que puedo cambiar completamente el renderizador simplemente creando otro objeto y asignándolo al valor ‘specificRenderer’ en renderer.py

¿Algunas ideas?

En renderer.py :

 import sys if __name__ != "__main__": sys.modules[__name__] = OpenGLRenderer() 

El nombre del módulo ahora se asigna a la instancia de OpenGLRenderer , y el import renderer en otros módulos obtendrá la misma instancia.

En realidad, ni siquiera necesitas el módulo separado. Usted puede simplemente hacer:

 import sys sys.modules["renderer"] = OpenGLRenderer() import renderer # gives current module access to the "module" 

… lo primero en tu módulo principal. Las importaciones de renderer en otros módulos, una vez más, se referirán a la misma instancia.

¿Estás seguro de que realmente quieres hacer esto en primer lugar? No es realmente como la gente espera que los módulos se comporten.

La forma más sencilla de hacerlo es tener main.py do

 from renderer import renderer 

en su lugar, a continuación, solo nombre el renderer specificRenderer .

Mi respuesta es muy similar a la de @ kindall, aunque tengo la idea de otro lado. Va un paso más allá en el sentido de que reemplaza el objeto de módulo que generalmente se coloca en la lista sys.modules con una instancia de una clase de su propio diseño. Como mínimo, una clase de este tipo tendría que verse así:

Archivo renderer.py :

 class _renderer(object): def __init__(self, specificRenderer): self.specificRenderer = specificRenderer def __getattr__(self, name): return getattr(self.specificRenderer, name) if __name__ != '__main__': import sys # from some_module import OpenGLRenderer sys.modules[__name__] = _renderer(OpenGLRenderer()) 

El __getattr__() simplemente reenvía la mayoría de los accesos de atributos al objeto renderizador real . La ventaja de este nivel de direccionamiento es que con él puede agregar sus propios atributos a la clase _renderer privada y acceder a ellos a través del objeto de renderer importado como si fuera parte de un objeto OpenGLRenderer . Si les da el mismo nombre que algo ya en un objeto OpenGLRenderer , en su lugar se les llamará, tienen libertad para reenviar, registrar, ignorar y / o modificar la llamada antes de transmitirla, lo que a veces puede ser muy útil.

Las instancias de clase colocadas en sys.modules son efectivamente singletons, por lo que si el módulo se importa en otros scripts en la aplicación, todos compartirán la instancia única creada por la primera.

Si no le importa que el import renderer un objeto en lugar de un módulo, entonces vea la solución shiny de kindall.

Si desea que funcione @property (es decir, cada vez que obtenga renderer.mytime , desea que se OpenGLRenderer.mytime a la función correspondiente a OpenGLRenderer.mytime ) y desea mantener el renderer como un módulo, es imposible. Ejemplo:

 import time class OpenGLRenderer(object): @property def mytime(self): return time.time() 

Si no le importan las propiedades, es decir, está bien que se mytime solo una vez (en el momento de carga del módulo), y seguirá devolviendo la misma marca de tiempo, entonces es posible hacerlo copiando todos los símbolos del objeto a el módulo:

 # renderer.py specificRenderer = OpenGLRenderer() for name in dir(specificRenderer): globals()[name] = getattr(specificRenderer, name) 

Sin embargo, esta es una copia de una sola vez. Si agrega métodos u otros atributos a specificRenderer más tarde de forma dinámica, o cambia algunos atributos más tarde, no se copiarán automáticamente en el módulo de renderer . Esto puede ser arreglado, sin embargo, por algún __setattr__ piratería __setattr__ fea.

Edit: Esta respuesta no hace lo que quiere el OP; no crea una instancia de un objeto y luego permite que las llamadas a un módulo se redirijan al mismo objeto. Esta respuesta se trata de cambiar el módulo de representación que se está utilizando.

Lo más fácil podría ser importar OpenGLRenderer en el progtwig main.py de esta manera:

 import OpenGLRenderer as renderer 

Ese es el código en un solo lugar, y en el rest de su módulo se puede hacer referencia a OpenGLRenderer como renderer .

Si tiene varios módulos como main.py, podría tener su archivo renderer.py en la misma línea:

 import OpenGLRenderer as renderer 

y luego otros módulos pueden usar

 from renderer import renderer 

Si OpenGLRenderer no está del todo bien, todavía puedes hacerlo para que funcione como lo necesitas en el módulo renderer.py.