Декоратор для регистрации методов Python в PyCLIPS

Я использую PyCLIPS для интеграции CLIPS в Python. Методы Python зарегистрированы в CLIPS с помощью clips.RegisterPythonFunction(method, optional-name), Поскольку мне нужно зарегистрировать несколько функций и сохранить код в чистоте, я ищу декоратора для регистрации.

Вот как это делается сейчас:

class CLIPS(object):
...
    def __init__(self, data):
        self.data = data
        clips.RegisterPythonFunction(self.pyprint, "pyprint")
    def pyprint(self, value):
        print self.data, "".join(map(str, value))

и вот как я хотел бы сделать это:

class CLIPS(object):
...
    def __init__(self, data):
        self.data = data
        #clips.RegisterPythonFunction(self.pyprint, "pyprint")
    @clips_callable
    def pyprint(self, value):
        print self.data, "".join(map(str, value))

Он хранит кодирование методов и регистрирует их в одном месте.

NB: я использую это в многопроцессорной установке, в которой процесс CLIPS выполняется в отдельном процессе, как это:

import clips
import multiprocessing

class CLIPS(object):
    def __init__(self, data):
        self.environment = clips.Environment()
        self.data = data
        clips.RegisterPythonFunction(self.pyprint, "pyprint")
        self.environment.Load("test.clp")
    def Run(self, cycles=None):
        self.environment.Reset()
        self.environment.Run()
    def pyprint(self, value):
        print self.data, "".join(map(str, value))

class CLIPSProcess(multiprocessing.Process):
    def run(self):
        p = multiprocessing.current_process()
        self.c = CLIPS("%s %s" % (p.name, p.pid))
        self.c.Run()

if __name__ == "__main__":
    p = multiprocessing.current_process()
    c = CLIPS("%s %s" % (p.name, p.pid))
    c.Run()
    # Now run CLIPS from another process
    cp = CLIPSProcess()
    cp.start()

3 ответа

Решение

Теперь он работает, используя декоратор для установки атрибута метода, который будет зарегистрирован в CLIPS, и используя inspect в init для выборки методов и их регистрации. Можно было бы также использовать некоторую стратегию именования, но я предпочитаю использовать декоратор, чтобы сделать регистрацию более явной. Функции Python могут быть зарегистрированы до инициализации среды CLIPS. Это то, что я сделал.

import inspect

def clips_callable(func):
    from functools import wraps
    @wraps(func)
    def wrapper(*__args,**__kw):
        return func(*__args,**__kw)
    setattr(wrapper, "clips_callable", True)
    return wrapper

class CLIPS(object):
    def __init__(self, data):
        members = inspect.getmembers(self, inspect.ismethod)
        for name, method in members:
            try:
                if method.clips_callable:
                    clips.RegisterPythonFunction(method, name)
            except:
                pass
...
    @clips_callable
    def pyprint(self, value):
        print self.data, "".join(map(str, value))

Для полноты код CLIPS в test.clp приведен ниже.

(defrule MAIN::start-me-up
    =>
    (python-call pyprint "Hello world")
)

Если кто-то знает более элегантный подход, пожалуйста, дайте мне знать.

Это должно быть довольно просто сделать так:

# mock clips for testing
class clips:
    @staticmethod
    def RegisterPythonFunction(func, name):
        print "register: ", func, name

def clips_callable(fnc):
    clips.RegisterPythonFunction(fnc, fnc.__name__)
    return fnc

@clips_callable
def test(self):
    print "test"

test()

edit: если используется в методе класса, он зарегистрирует только несвязанный метод. Так что это не будет работать, если функция будет вызываться без экземпляра класса в качестве первого аргумента. Поэтому это было бы полезно для регистрации функций уровня модуля, но не методов класса. Для этого вам необходимо зарегистрировать их в __init__,

Кажется, что элегантное решение, предложенное mata, не сработает, потому что среда CLIPS должна быть инициализирована до регистрации в ней методов.
Я не эксперт по Python, но из некоторых поисков кажется, что комбинация inspect.getmembers() и hasattr() поможет вам - вы можете зациклить всех членов вашего класса и зарегистрировать тех, которые имеют @clips_callable приписать к клипам.

Другие вопросы по тегам