سؤال

لديّ برنامج يحتاج أحيانًا إلى استدعاء Python من أجل تكوين بعض المهام. أحتاج إلى وظيفة تستدعي بيثون و يمسك الثعبان stdout ويضعه في بعض الملفات. هذا إعلان عن الوظيفة

  pythonCallBackFunc(const char* pythonInput)

مشكلتي هي التقاط كل الإخراج Python لأمر معين (Pythoninput). ليس لدي أي خبرة مع Python API ولا أعرف ما هي الأسلوب المناسب للقيام بذلك. أول شيء جربته هو إعادة توجيه sdtout من Python و STDERR باستخدام PY_RUN_SIMPLESTRING هذا مثال على الرمز الذي كتبته.

#include "boost\python.hpp"
#include <iostream>

void pythonCallBackFunc(const char* inputStr){   

    PyRun_SimpleString(inputStr); 
}


int main () {
    ...
   //S0me outside functions does this
   Py_Initialize();
   PyRun_SimpleString("import sys");
   PyRun_SimpleString("old_stdout = sys.stdout");
   PyRun_SimpleString("fsock = open('python_out.log','a')");
   PyRun_SimpleString("sys.stdout = fsock");
   ...

   //my func   
   pythonCallBackFunc("print 'HAHAHAHAHA'");
   pythonCallBackFunc("result = 5");
   pythonCallBackFunc("print result");

   pythonCallBackFunc("result = 'Hello '+'World!'");
   pythonCallBackFunc("print result");

   pythonCallBackFunc("'KUKU '+'KAKA'");
   pythonCallBackFunc("5**3");

   pythonCallBackFunc("prinhghult");

   pythonCallBackFunc("execfile('stdout_close.py')");
   ... 

   //Again anothers function code
   PyRun_SimpleString("sys.stdout = old_stdout");
   PyRun_SimpleString("fsock.close()");

   Py_Finalize();
   return 0;
}

هل هناك طريقة أفضل للقيام بذلك؟ علاوة على ذلك ، لسبب ما pyrun_simplestring لا يفعل شيئًا عندما يحصل على بعض التعبير الرياضي ، على سبيل المثال pyrun_simplestring ("5 ** 3") لا يطبع أي شيء (Python conlsul يطبع النتيجة: 125)

ربما يكون من المهم ، أنا أستخدم Visual Studio 2008. شكرًا ، أليكس


التغييرات التي أجريتها وفقًا لمارك اقتراح:

  #include <python.h>
  #include <string>

  using namespace std;

  void PythonPrinting(string inputStr){ 
     string stdOutErr =
    "import sys\n\
     class CatchOut:\n\
        def __init__(self):\n\
           self.value = ''\n\
        def write(self, txt):\n\
           self.value += txt\n\
     catchOut = CatchOut()\n\
     sys.stdout = catchOut\n\
     sys.stderr = catchOut\n\
    "; //this is python code to redirect stdouts/stderr

     PyObject *pModule = PyImport_AddModule("__main__"); //create main module
     PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect

     PyRun_SimpleString(inputStr.c_str());
     PyObject *catcher = PyObject_GetAttrString(pModule,"catchOut");

     PyObject *output = PyObject_GetAttrString(catcher,"value");
     printf("Here's the output: %s\n", PyString_AsString(output)); 
     }

  int main(int argc, char** argv){
         Py_Initialize();

     PythonPrinting("print 123");
     PythonPrinting("1+5");
     PythonPrinting("result = 2");
         PythonPrinting("print result");

         Py_Finalize();
         return 0;
  }

الإخراج الذي أحصل عليه بعد التشغيل الرئيسي:

 Here's the output: 123

 Here's the output:
 Here's the output: 
 Here's the output: 2

إنه جيد بالنسبة لي ، لكن مشكلة واحدة فقط ، يجب أن تكون كذلك

 Here's the output: 123

 Here's the output: 6

 Here's the output: 
 Here's the output: 2

لا أعرف لماذا ولكن بعد تشغيل هذا الأمر: Pythonprinting ("1+5") ، فإن الأمر pystring_asstring (الإخراج) يرجع سلسلة فارغة (char*) بدلاً من 6 ... :( هل هناك شيء لا أستطيع أن أفقد هذا انتاج؟

ثاكس ، أليكس

هل كانت مفيدة؟

المحلول

إذا كنت أقرأ سؤالك بشكل صحيح ، فأنت تريد التقاط stdout/stderr في متغير داخل C ++ الخاص بك؟ يمكنك القيام بذلك عن طريق إعادة توجيه stdout/stderr إلى متغير python ثم الاستعلام عن هذا المتغير في c ++ الخاص بك. من فضلك لا أنني لم أفعل عد المرجع الصحيح أدناه:

#include <Python.h>
#include <string>

int main(int argc, char** argv)
{
    std::string stdOutErr =
"import sys\n\
class CatchOutErr:\n\
    def __init__(self):\n\
        self.value = ''\n\
    def write(self, txt):\n\
        self.value += txt\n\
catchOutErr = CatchOutErr()\n\
sys.stdout = catchOutErr\n\
sys.stderr = catchOutErr\n\
"; //this is python code to redirect stdouts/stderr

    Py_Initialize();
    PyObject *pModule = PyImport_AddModule("__main__"); //create main module
    PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect
    PyRun_SimpleString("print(1+1)"); //this is ok stdout
    PyRun_SimpleString("1+a"); //this creates an error
    PyObject *catcher = PyObject_GetAttrString(pModule,"catchOutErr"); //get our catchOutErr created above
    PyErr_Print(); //make python print any errors

    PyObject *output = PyObject_GetAttrString(catcher,"value"); //get the stdout and stderr from our catchOutErr object

    printf("Here's the output:\n %s", PyString_AsString(output)); //it's not in our C++ portion

    Py_Finalize();


    return 0;

}

نصائح أخرى

هنا حل ودود C ++ لقد طورته مؤخرًا.

أشرح بعض التفاصيل عنها على مدونتي: Python sys.stdout إعادة توجيه في C ++ حيث أشير أيضًا إلى المستودع في GitHub حيث يمكن العثور على أحدث الإصدار. فيما يلي مثال كامل بناءً على الكود الحالي في وقت نشر هذه الإجابة:

#include <functional>
#include <iostream>
#include <string>
#include <Python.h>

namespace emb
{

typedef std::function<void(std::string)> stdout_write_type;

struct Stdout
{
    PyObject_HEAD
    stdout_write_type write;
};

PyObject* Stdout_write(PyObject* self, PyObject* args)
{
    std::size_t written(0);
    Stdout* selfimpl = reinterpret_cast<Stdout*>(self);
    if (selfimpl->write)
    {
        char* data;
        if (!PyArg_ParseTuple(args, "s", &data))
            return 0;

        std::string str(data);
        selfimpl->write(str);
        written = str.size();
    }
    return PyLong_FromSize_t(written);
}

PyObject* Stdout_flush(PyObject* self, PyObject* args)
{
    // no-op
    return Py_BuildValue("");
}

PyMethodDef Stdout_methods[] =
{
    {"write", Stdout_write, METH_VARARGS, "sys.stdout.write"},
    {"flush", Stdout_flush, METH_VARARGS, "sys.stdout.flush"},
    {0, 0, 0, 0} // sentinel
};

PyTypeObject StdoutType =
{
    PyVarObject_HEAD_INIT(0, 0)
    "emb.StdoutType",     /* tp_name */
    sizeof(Stdout),       /* tp_basicsize */
    0,                    /* tp_itemsize */
    0,                    /* tp_dealloc */
    0,                    /* tp_print */
    0,                    /* tp_getattr */
    0,                    /* tp_setattr */
    0,                    /* tp_reserved */
    0,                    /* tp_repr */
    0,                    /* tp_as_number */
    0,                    /* tp_as_sequence */
    0,                    /* tp_as_mapping */
    0,                    /* tp_hash  */
    0,                    /* tp_call */
    0,                    /* tp_str */
    0,                    /* tp_getattro */
    0,                    /* tp_setattro */
    0,                    /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,   /* tp_flags */
    "emb.Stdout objects", /* tp_doc */
    0,                    /* tp_traverse */
    0,                    /* tp_clear */
    0,                    /* tp_richcompare */
    0,                    /* tp_weaklistoffset */
    0,                    /* tp_iter */
    0,                    /* tp_iternext */
    Stdout_methods,       /* tp_methods */
    0,                    /* tp_members */
    0,                    /* tp_getset */
    0,                    /* tp_base */
    0,                    /* tp_dict */
    0,                    /* tp_descr_get */
    0,                    /* tp_descr_set */
    0,                    /* tp_dictoffset */
    0,                    /* tp_init */
    0,                    /* tp_alloc */
    0,                    /* tp_new */
};

PyModuleDef embmodule =
{
    PyModuleDef_HEAD_INIT,
    "emb", 0, -1, 0,
};

// Internal state
PyObject* g_stdout;
PyObject* g_stdout_saved;

PyMODINIT_FUNC PyInit_emb(void) 
{
    g_stdout = 0;
    g_stdout_saved = 0;

    StdoutType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&StdoutType) < 0)
        return 0;

    PyObject* m = PyModule_Create(&embmodule);
    if (m)
    {
        Py_INCREF(&StdoutType);
        PyModule_AddObject(m, "Stdout", reinterpret_cast<PyObject*>(&StdoutType));
    }
    return m;
}

void set_stdout(stdout_write_type write)
{
    if (!g_stdout)
    {
        g_stdout_saved = PySys_GetObject("stdout"); // borrowed
        g_stdout = StdoutType.tp_new(&StdoutType, 0, 0);
    }

    Stdout* impl = reinterpret_cast<Stdout*>(g_stdout);
    impl->write = write;
    PySys_SetObject("stdout", g_stdout);    
}

void reset_stdout()
{
    if (g_stdout_saved)
        PySys_SetObject("stdout", g_stdout_saved);

    Py_XDECREF(g_stdout);
    g_stdout = 0;
}

} // namespace emb

int main()
{
    PyImport_AppendInittab("emb", emb::PyInit_emb);
    Py_Initialize();
    PyImport_ImportModule("emb");

    PyRun_SimpleString("print(\'hello to console\')");

    // here comes the ***magic***
    std::string buffer;
    {
        // switch sys.stdout to custom handler
        emb::stdout_write_type write = [&buffer] (std::string s) { buffer += s; };
        emb::set_stdout(write);
        PyRun_SimpleString("print(\'hello to buffer\')");
        PyRun_SimpleString("print(3.14)");
        PyRun_SimpleString("print(\'still talking to buffer\')");
        emb::reset_stdout();
    }

    PyRun_SimpleString("print(\'hello to console again\')");
    Py_Finalize();

    // output what was written to buffer object
    std::clog << buffer << std::endl;
}

هذا يسمح بالاعتراض sys.stdout.write الإخراج مع أي نوع من كيان C ++ القابل للاتصال: وظيفة مجانية ، وظيفة عضو الفئة ، كائنات الوظيفة المسماة أو حتى وظائف مجهولة كما في المثال أعلاه حيث أستخدم C ++ 11 Lambda.

لاحظ أن هذا مثال أدنى لتقديم المفهوم الأساسي. في رمز جاهز للإنتاج ، يحتاج بالتأكيد إلى مزيد من الاهتمام حول العد المرجعي لـ PyObject, والتخلص من الدولة العالمية ، وهلم جرا.

أعلم أن هذا السؤال قديم ، ولكن لم يتم الإجابة على جزء واحد من السؤال بعد:

"كيفية التقاط إخراج الأوامر التي لا تكتب مباشرة إلى stdout of Python ، مثل: 1+1؟"

فيما يلي الخطوات (لـ Python 3.4):

  1. إعادة توجيه stdout/stderr إلى متغير python باستخدام حل Mark: https://stackoverflow.com/a/430737/1046299

  2. وظيفة نسخ PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags) من كود المصدر بيثون. يقع في الملف pythonrun.c

  3. تعديل PyRun_InteractiveOneObject اسم الوظيفة والتوقيع بحيث تأخذ الوظيفة الجديدة ملف const char* (أمرك) كمعلمة أولى بدلاً من أ FILE*. ثم ستحتاج إلى استخدام PyParser_ASTFromStringObject بدلاً من PyParser_ASTFromFileObject في تنفيذ الوظيفة. لاحظ أنك ستحتاج إلى نسخ الوظيفة run_mod كما هي من Python لأنه يسمى في الوظيفة.

  4. اتصل بالوظيفة الجديدة مع أمرك ، على سبيل المثال 1+1. يجب أن تتلقى stdout الآن الإخراج 2.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top