4

一連の関数を実装しました。それらは、Python インタープリターによって呼び出された同じ C 関数からディスパッチされます。

PyObject *
CmdDispatch(PyObject *self, PyObject *args, PyObject *kwargs)

予期せず、self が NULL であり、現在呼び出されている関数名を取得する必要があります。この情報を取得する方法はありますか?

私はすべてこのルーチンを通過している数十の機能を持っています。このコマンドは、すべてのオプションを C++ マップに処理し、それを各コマンドの実装に渡します。

更新: http://docs.python.org/extending/extending.html#a-simple-example具体的には、「self 引数は、モジュール レベルの関数のモジュール オブジェクトを指します。メソッドの場合は、オブジェクト インスタンスを指します。 ." ですが、python 2.6 に対してリンクすると null が渡されます。

4

4 に答える 4

4

私は非常によく似た問題を解決しようとしています。

私が到達した結論は、Python C API のレベルで現在の関数または呼び出し元の名前を特定する方法がないことを示唆しています。その理由は、Python インタープリターは純粋な Python 関数 (Python 自体に実装されている) のみをコール スタックに置くためです。C で実装された関数は、モジュール メソッド テーブルに登録されているかどうかに関係なく、Python スタックに置かれないため、スタック フレームを調べても見つけることができません。

これは、私が達成したかったことを示す Python の簡単な例です (Juan が同様の動作を求めていると思います)。

import sys
def foo():
    print('foo:', sys._getframe(0).f_code.co_name)
def bar():
    print('bar:', sys._getframe(0).f_code.co_name)
    foo()
bar()

以下は、この例 ( Python 3 docsに基づく) とほぼ同じですが、Python C API を使用して実装されています。

// Based on example from Python 3.2.1 documentation, 5.4. Extending Embedded Python
// http://docs.python.org/release/3.2.1/extending/embedding.html#extending-embedded-python
//
#include <Python.h>
#include <frameobject.h>

static void foo()
{
    PyThreadState * ts = PyThreadState_Get();
    PyFrameObject* frame = ts->frame;
    while (frame != 0)
    {
        char const* filename = _PyUnicode_AsString(frame->f_code->co_filename);
        char const* name = _PyUnicode_AsString(frame->f_code->co_name);
        printf("foo: filename=%s, name=%s\n", filename, name);
        frame = frame->f_back;
    }
}

static void bar()
{
    PyRun_SimpleString(
    "import sys\n"
    "print(\"bar: filename=%s, name=%s\" % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_code.co_name))"
    );
}

static PyObject* emb_numargs(PyObject *self, PyObject *args)
{
    foo();
    bar();

    PyRun_SimpleString(
    "import sys\n"
    "print(\"emb_numargs: filename=%s, name=%s\" % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_code.co_name))"
    );

    return PyLong_FromLong(0);
}

static PyMethodDef EmbMethods[] = {
    {"numargs", emb_numargs, METH_VARARGS, "Return number 0"},
    {NULL, NULL, 0, NULL}
};

static PyModuleDef EmbModule = {
    PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
    NULL, NULL, NULL, NULL
};

static PyObject* PyInit_emb(void)
{
    return PyModule_Create(&EmbModule);
}

int main(int argc, char* argv[])
{
    PyImport_AppendInittab("emb", &PyInit_emb);
    Py_Initialize();
    PyRun_SimpleString(
    "import emb\n"
    "print('This is Zero: ', emb.numargs())\n"
    );
    Py_Finalize();
    return 0;
}

これでネッドの答えも完成することを願っています。

于 2011-08-25T14:32:18.780 に答える
2

Python APIは、呼び出している関数を通知するようには構築されていません。関数を作成し、それを呼び出している場合、APIは、作成した関数がわかっていることを前提としています。Python関数ごとに小さなラッパー関数を作成する必要があります。これを行う最良の方法は、1つのC関数を最初の引数として文字列を受け取る1つのPython関数として登録することです。次に、Pythonレイヤーで、必要な数のPython関数を作成します。各関数は、実際に呼び出したい関数を識別する適切な文字列引数を使用してC関数を呼び出します。

もう1つの方法は、Cコードの構造を再考し、必要な数の関数を用意することです。各関数は、オプションなどを処理するために共通のヘルパーコードを呼び出します。

于 2011-07-04T00:58:26.577 に答える
0

C-API から直接実行できるかどうかはわかりません。最悪の場合、tracebackC からモジュールを呼び出して、呼び出し元の名前を取得することもできます。

于 2011-07-04T07:13:38.800 に答える