Как присвоить классу C++ функцию Python __repr__() с помощью SWIG

Я заметил, что когда кто-то печатает

help

в ответе на Python можно получить

Type help() for interactive help, ...

и когда один тип

help()

один попадает в режим help. Я почти уверен, что это потому, что site._Helper определяет __repr__() (для первого примера) и __call__() (для второго).

Мне нравится такое поведение (запрашивать только объект и вызываемый синтаксис), и я хотел бы сделать то же самое для класса C++, который я экспортирую в Python через SWIG. Вот простой пример того, что я пытался сделать

helpMimic.h
-----------
class HelpMimic
{
public:
    HelpMimic() {};
    ~HelpMimic() {};

    char *__repr__();
    void operator()(const char *func=NULL);
};

helpMimic.cxx
-------------
char *HelpMimic::__repr__()
{
    return "Online help facilities are not yet implemented.";
}

void HelpMimic::operator()(const char *func)
{
    log4cxx::LoggerPtr transcriptPtr = oap::getTranscript();
    std::string commentMsg("# Online help facilities are not yet implemented. Cannot look up ");
    if (func) {
        commentMsg += func;
    }
    else {
        commentMsg += "anything.";
    }

    LOG4CXX_INFO(transcriptPtr, commentMsg);
}

helpMimic.i
-----------
%module sample
 %{
#include <helpMimic.h>
 %}
class HelpMimic
{
public:
    HelpMimic() {};
    ~HelpMimic() {};

    char *__repr__();
    void operator()(const char *func=NULL);
};

Когда я пытаюсь использовать этот класс в своем приложении, я не могу получить поведение, которое я вижу с помощью help (выходные данные ниже взяты из приложения C++ со встроенным Python, где каждая входная строка отправлено через PyEval_String()):

 tam = sample.HelpMimic()
 tam   # echoes 'tam', nothing else
 print tam
 # _5010b70200000000_p_HelpMimic
 print repr(tam)
 # <Swig Object of type 'HelpMimic *' at 0x28230a0>
 print tam.__repr__()
 # Online help facilities are not yet implemented.

Этот последний отпечаток показывает, что метод __repr__() существует, но я не могу найти его, используя более простую ссылку на объект или используя repr(tam). Я также пытался определить __str()__ в надежде, что я неправильно понял, что будет вызвано, но все равно не повезло.

Я пытался использовать директиву %extend в файле интерфейса, чтобы вставить определение __str__() или __repr__() в файл определения интерфейса SWIG вместо того, чтобы определять их непосредственно в C++, но безрезультатно.

Что мне не хватает?


person Don Wakefield    schedule 21.02.2013    source источник
comment
Только что попробовал ваш код, и это просто отлично для меня. Я закомментировал строки LOG4CXX*, потому что у меня нет необходимого материала для компиляции, но в остальном я ничего не менял. И это сработало... Для справки, я использую python2.7 на OSX10.8. Понятия не имею, чем моя установка отличается от вашей. Казалось бы, что бы вы ни упустили, это не связано с самим кодом.   -  person entropy    schedule 22.02.2013
comment
Спасибо. Я надеюсь, что смогу узнать, что отличается от того, что вызывает результат, который я вижу...   -  person Don Wakefield    schedule 22.02.2013
comment
Согласен, что это очень странно...   -  person entropy    schedule 22.02.2013
comment
Как насчет определения repr как const char* __repr__() const?   -  person Oliver    schedule 13.08.2013
comment
Возможно, оболочка Python скрывает версию C++ repr. Вы можете попробовать определить обычный const char* printRepr() const в C++, экспортировать его через файл .i и в этом файле .i расширить класс с помощью метода Python repr, который вызывает Python printRepr (который обертывает метод C++ ).   -  person Oliver    schedule 13.08.2013
comment
Вы используете -builtin при вызове swig для этого?   -  person Flexo    schedule 23.05.2015


Ответы (2)


Как предложил @flexo в комментарии, если вы используете флаг -builtin к генератору кода SWIG, repr() не будет вызывать ваш метод __repr__. Вместо этого вам нужно определить функцию, которая подходит для слота repr.

%feature("python:slot", "tp_repr", functype="reprfunc") HelpMimic::printRepr;

Согласно HelpMimic::printRepr должна иметь подпись, соответствующую ожидаемой подписи (tp_repr в документации по Python) — он должен возвращать строку или объект Unicode. Еще одно предостережение — вы не можете поместить одну и ту же функцию более чем в один слот, поэтому не пытайтесь использовать это для tp_str!

person dbn    schedule 02.06.2015
comment
Для меня в будущем (или любого, кто находится в похожей ситуации, как у меня): синтаксис, особенно часть HelpMimic::printRepr, также применяется, когда вы привязываете библиотеку C к Python. Не забудьте %extend добавить в свой класс функцию printRepr. Имя расширяемого класса можно получить из сгенерированного кода C или найти соответствующую структуру в библиотеке C для привязки. - person Frederick Nord; 22.11.2016

Обычно я использую функцию %extend, чтобы не слишком сильно приспосабливать C/C++ к конкретному целевому языку. Например.

%extend MyClass {
  %pythoncode %{
    def __repr__(self):
      # How you want your object to be shown
    __swig_getmethods__["someMember"] = SomeMemberGet
    __swig_setmethods__["someMember"] = SomeMemberSet
    if _newclass:
      someMember = property(SomeMemberGet,SomeMemberSet)
    def show(self):
      # You could possibly visualize your object using matplotlib
  %}
};

Где вы внутри функции repr можете вызывать практически любую функцию и форматировать вывод в соответствии с вашими потребностями. Кроме того, вы можете добавлять свойства и определять, как они сопоставляются с сеттерами и геттерами.

person Jens Munk    schedule 11.08.2015
comment
Кажется, это не работает, когда используется флаг SWIG -builtin. - person Frederick Nord; 21.11.2016
comment
Это не так. Документация SWIG также говорит об этом. Я бы рекомендовал вам полностью избегать использования встроенного флага - person Jens Munk; 21.11.2016
comment
Можете ли вы уточнить вашу рекомендацию? - person Frederick Nord; 22.11.2016
comment
Я обновлю ответ примерами свойств и функций чтения/записи. - person Jens Munk; 22.11.2016