Перехватывать звонки модуля? - PullRequest
2 голосов
/ 09 декабря 2010

Я пытаюсь «перехватить» все вызовы определенного модуля и перенаправить их на другой объект.Я хотел бы сделать это, чтобы у меня была довольно простая архитектура плагинов.

Например, в 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')

Это означает, что любая функция может просто импортировать рендер и использовать его, не беспокоясь о деталях.Это также означает, что я могу полностью изменить средство визуализации, просто создав другой объект и присвоив ему значение «specificRenderer» в renderer.py

Любые идеи?

Ответы [ 5 ]

4 голосов
/ 09 декабря 2010

In 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 в другие модули, опять же, будет ссылаться на тот же экземпляр.

Вы уверены, что действительно хотите сделать это в первую очередь?Это не совсем то, как люди ожидают, что модули будут вести себя.

2 голосов
/ 09 декабря 2010

Мой ответ очень похож на @ kindall's, хотя я понял эту идею в другом месте.Это еще один шаг в том смысле, что он заменяет объект модуля, который обычно помещается в список 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, фактически являются одиночными, поэтому, если модуль импортируется в другие скрипты приложения, они все будут использовать один экземпляр, созданный первым.

2 голосов
/ 09 декабря 2010

Самый простой способ сделать это - вместо main.py сделать

from renderer import renderer

, а затем просто набрать specificRenderer renderer.

0 голосов
/ 09 декабря 2010

Если вы не возражаете, что 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__ взлома.

0 голосов
/ 09 декабря 2010

Изменить: Этот ответ не делает то, что хочет ОП;он не создает экземпляр объекта, а затем позволяет перенаправлять вызовы модуля на тот же объект.Этот ответ об изменении, какой модуль рендеринга используется.

Проще всего импортировать OpenGLRenderer в программу main.py следующим образом:

import OpenGLRenderer as renderer

Это код только в одном месте, а в остальной части вашего модуля можно обратиться к OpenGLRenderer.as renderer.

Если у вас есть несколько модулей, таких как main.py, ваш файл renderer.py может иметь такую ​​же строку:

import OpenGLRenderer as renderer

, и тогда другие модули могут использовать

from renderer import renderer

Если OpenGLRenderer еще не совсем крякнул, вы можете установить его, чтобы он работал, как вам нужно в модуле renderer.py.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...