拦截模块电话?

时间:2010-12-08 22:06:30

标签: python module

我正试图“拦截”对特定模块的所有调用,并将它们重新路由到另一个对象。我想这样做,以便我可以拥有一个相当简单的插件架构。

例如,在main.py

import renderer

renderer.draw('circle')

在renderer.py

specificRenderer = OpenGLRenderer()

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

这意味着任何函数都可以只导入渲染器并使用它,而不必担心细节。这也意味着我可以通过创建另一个对象并将其分配给renderer.py中的'specificRenderer'值来完全更改渲染器

有什么想法吗?

5 个答案:

答案 0 :(得分:4)

renderer.py

import sys

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

模块名称现在映射到OpenGLRenderer实例,而其他模块中的import renderer将获得相同的实例。

实际上,您甚至不需要单独的模块。你可以这样做:

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

...主要模块中的第一件事。再次,在其他模块中导入renderer将引用相同的实例。

你确定你真的想在第一时间这样做吗?人们对模块的行为并不是真的如此。

答案 1 :(得分:2)

最简单的方法是让main.py执行

from renderer import renderer

相反,只需命名为specificRenderer renderer

答案 2 :(得分:2)

我的答案与@ kindall非常相似,尽管我在其他地方得到了这个想法。它更进一步,它将通常放在sys.modules列表中的模块对象替换为您自己设计的类的实例。至少这样的课程需要看起来像这样:

档案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())

__getattr__()方法只是将大多数属性访问转发到真实的渲染器对象。这种间接级别的优势在于,您可以将自己的属性添加到私有_renderer类,并通过导入的renderer对象访问它们,就像它们是OpenGLRenderer的一部分一样。对象。如果你给它们与OpenGLRenderer对象中已经存在的名称相同,那么它们将被调用,可以在传递之前自由转发,记录,忽略和/或修改它 - 有时可能是非常方便。

放置在sys.modules中的类实例实际上是单例,因此如果在应用程序的其他脚本中导入模块,它们将共享由第一个创建的单个实例。

答案 3 :(得分:0)

编辑:这个答案不符合OP的要求;它不实例化一个对象,然后让对模块的调用重定向到同一个对象。这个答案是关于改变使用哪个渲染模块。

最简单的可能是在main.py程序中导入OpenGLRenderer,如下所示:

import OpenGLRenderer as renderer

这个代码只在一个地方,在你的模块的其余部分中,OpenGLRenderer可以称为renderer

如果您有几个模块,例如main.py,您可以将renderer.py文件放在同一行:

import OpenGLRenderer as renderer

然后其他模块可以使用

from renderer import renderer

如果OpenGLRenderer还没有完全嘎嘎叫,你可以在renderer.py模块中根据需要对它进行monkeypatch工作。

答案 4 :(得分:0)

如果您不介意import renderer导致对象而不是模块,那么请参阅kindall的精彩解决方案。

如果您希望@property正常工作(即每次提取renderer.mytime,您希望调用与OpenGLRenderer.mytime对应的功能),并且您希望保留renderer作为一个模块,那是不可能的。例如:

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

如果您不关心属性,即mytime仅被调用一次(在模块加载时),并且它将继续返回相同的时间戳,那么可以通过将所有符号从对象复制到模块:

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

然而,这是一次性复制。如果稍后动态地向specificRenderer添加方法或其他属性,或稍后更改某些属性,则它们不会自动复制到renderer模块。但是,这可以通过一些丑陋的__setattr__黑客来解决。