【问题标题】:CPython 'overloaded' functionsCPython“重载”函数
【发布时间】:2020-03-23 09:24:46
【问题描述】:

我正在尝试重载一个 python 扩展函数,该函数将接受一个对象或一个字符串。

typedef struct
{
  PyObject_HEAD
} CustomObject;

PyObject* customFunction(CustomObject* self, PyObject* args);

PyMethodDef methods[] =
{
 {"customFunction", (PyCFunction) customFunction, METH_VARAGS, "A custom function"},
 {NULL}
}

PyTypeObject TypeObj =
{
  PyVarObject_HEAD_INIT(NULL, 0)
  .tp_name = "customModule.CustomObject",
  .tp_doc = "Custom Object",
  .tp_basicsize = sizeof(CustomObject),
  .tp_itemsize = 0,
  .tp_flags = Py_TPFLAGS_DEFAULT,
  .tp_methods = methods,
}

// Area of problem
PyObject* customFunction(CustomObject* self, PyObject* args)
{
  const char* string;
  PyObject* object;
  if (PyArg_ParseTuple(args, "O!", &TypeObj, &object)) // TypeObj is the PyTypeObject fpr CustomObject
  {
    std::cout << "Object function\n"
    // Do whatever and return PyObject*
  }
  else if (PyArg_ParseTuple(args, "s", &string))
  {
    std::cout << "String function\n"
    // Do whatever and return PyObject*
  }
  return PyLong_FromLong(0); // In case nothing above works
}

在 python 中,我尝试了除函数之外的其他方法,但出现此错误Error: &lt;built-in method customFunction of CustomModule.CustomObject object at 0xmemoryadress&gt; returned a result with an error set

这里是这个 PyArg_ParseTuple 的 Python 文档:

int PyArg_ParseTuple(PyObject *args, const char *format, ...)

将只接受位置参数的函数的参数解析为局部变量。成功返回真;失败时,它返回 false 并引发相应的异常

我猜 PyArg_ParseTuple 设置了一个错误,这导致整个函数无法工作(我的模块方法表中确实有 customFunction,我只是省略了该代码)。如果我有以下 Python:

import CustomModule

try:
  CustomModule.customFunction("foo")
except Exception as e:
  print("Error:", e)

String function 确实被输出了,所以字符串 if 语句中的代码确实有效,但我认为发生错误是因为对象的 PyArg_ParseTuple 失败,所以它返回一个错误(不是 100% 确定这是否正确)。

有什么方法可以防止 PyArg_ParseTuple() 引发错误,是否有其他函数,或者是否有更好的方法来“重载”我的自定义函数?

【问题讨论】:

  • “失败时,它返回 false 并引发适当的异常”——你可以在 Python 中做到这一点吗?今天我学会了。引发异常返回一个值。有趣。
  • 请提供minimal reproducible example。你还没有向我们展示TypeObj 是如何实例化的
  • @JesperJuhl:据我了解,这类似于使用错误代码。例如,PyErr_SetString
  • @JesperJuhl PyArg_ParseTuple 是一个 C 函数。如果出现问题,它会返回一个 C 0 值,然后设置 Python 异常状态。这不是你可以在 Python 中做的事情 - 这就是 Python 异常在 C 中实现的方式。
  • @DavidW 谢谢你的解释。

标签: python c++ overloading cpython python-c-api


【解决方案1】:

我可能只是使用PyArg_ParseTuple 来获取一个通用的未指定对象,然后稍后使用Py*_Check 处理对象类型:

if (!PyArg_ParseTuple(args, "O", &object)) {
    return NULL;
}
if (PyObject_IsInstance(object, (PyObject*)&PyType)) { // or a more specific function if one exists
    std::cout << "Object function\n";
} else if (PyUnicode_Check(object)) {
    std::cout << "String function\n";
} else {
    // set an error, return NULL
}

这是因为 Python 的“请求宽恕,而不是许可”模式

try:
    something()
except SomeException:
    somethingElse()

不能很好地转换成 C,并且涉及到相当多的代码来处理异常。如果您真的想这样做,那么您需要在第二个PyArg_ParseTuple 之前调用PyErr_Clear,理想情况下,您应该检查它是否是您认为的异常,而不是完全其他。 p>

【讨论】:

  • 如何将 CustomObject 表示为 PyObject* 类?
  • 啊 - CustomObjectPyTypeObject。你只需要投射它:(PyObject*)&amp;CustomObjectPyTypeObjectPyObject 的变体,所以没关系。
  • 小心 - 我错过了&amp;。确保复制正确的编辑!
  • 最后评论:由于您使用的是 C++,您可能更喜欢 C++ 风格的演员 reinterpret_cast&lt;PyObject*&gt;(&amp;CustomObject)。它的运行方式完全相同,但有些人更喜欢它更清晰。
猜你喜欢
  • 2018-08-26
  • 2014-06-18
  • 1970-01-01
  • 2012-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多