Python C扩展:如何记录方法签名?
我正在写C语言扩展,想让我的方法签名在查看时能被看到。
static PyObject* foo(PyObject *self, PyObject *args) {
/* blabla [...] */
}
PyDoc_STRVAR(
foo_doc,
"Great example function\n"
"Arguments: (timeout, flags=None)\n"
"Doc blahblah doc doc doc.");
static PyMethodDef methods[] = {
{"foo", foo, METH_VARARGS, foo_doc},
{NULL},
};
PyMODINIT_FUNC init_myexample(void) {
(void) Py_InitModule3("_myexample", methods, "a simple example module");
}
现在,如果我在构建完成后加载这个模块,然后查看它的帮助信息:
>>> import _myexample
>>> help(_myexample)
我会看到:
Help on module _myexample:
NAME
_myexample - a simple example module
FILE
/path/to/module/_myexample.so
FUNCTIONS
foo(...)
Great example function
Arguments: (timeout, flags=None)
Doc blahblah doc doc doc.
我希望能更具体一些,把foo(...)替换成foo(timeout, flags=None)。
我能做到这一点吗?怎么做呢?
3 个回答
如果这对你有帮助,我发现下面这样的写法可以提供一个类型签名,并且让PyCharm理解这些类型。
对于一个应该有以下签名的函数(第二个参数是可选的):
def function(paramA: str, paramB: bool = False) -> str:
我会在PyMethodDef的ml_doc字段中使用以下内容:
"function(paramA, /, paramB=False)\n--\nn"
"function(paramA: str, paramB: bool=False) -> str\n"
":param str paramA: blah blah blah\n"
":param str paramB: blah blah blah\n"
":returns: blah blah blah\n"
":rtype: str\n\n"
"some narrative description of the function"
在"\n--\n\n"之前的所有内容都是用来让Python填充__text_signature__
这个方法的。剩下的内容则放进__doc__
里。PyCharm会解析__doc__
来判断类型。:rtype:
这一行末尾的双换行是必要的,这样PyCharm才能正确处理,否则后面的描述会被附加到这一行上。
已经过去7年了但你可以为C扩展函数和类添加签名。
Python本身使用Argument Clinic来动态生成签名。然后一些机制会创建一个__text_signature__
,这个可以被查看(比如用help
命令)。@MartijnPieters在这个回答中很好地解释了这个过程。
你其实可以从Python中获取参数诊所,并以动态的方式来做,但我更喜欢手动的方法:把签名添加到文档字符串中:
在你的情况下:
PyDoc_STRVAR(
foo_doc,
"foo(timeout, flags=None, /)\n"
"--\n"
"\n"
"Great example function\n"
"Arguments: (timeout, flags=None)\n"
"Doc blahblah doc doc doc.");
我在我的包中大量使用了这个:iteration_utilities/src
。为了演示它是有效的,我使用了这个包中暴露的一个C扩展函数:
>>> from iteration_utilities import minmax
>>> help(minmax)
Help on built-in function minmax in module iteration_utilities._cfuncs:
minmax(iterable, /, key, default)
Computes the minimum and maximum values in one-pass using only
``1.5*len(iterable)`` comparisons. Recipe based on the snippet
of Raymond Hettinger ([0]_) but significantly modified.
Parameters
----------
iterable : iterable
The `iterable` for which to calculate the minimum and maximum.
[...]
这个函数的文档字符串在这个文件中定义。
重要的是要意识到在Python 3.4之前这是不可能的,而且你需要遵循一些规则:
你需要在签名定义行后面加上
--\n\n
。签名必须在文档字符串的第一行。
签名必须是有效的,比如
foo(a, b=1, c)
是不行的,因为在有默认值的参数后面不能再定义位置参数。你只能提供一个签名。所以如果你使用类似下面的方式就不行:
foo(a) foo(x, a, b) -- Narrative documentation
我通常处理这类问题的方法是:“查看源代码”。
简单来说,我会假设Python的标准模块在有这种功能时会使用它。查看源代码(比如这里)应该会有帮助,但实际上即使是标准模块也会在自动输出后添加原型。就像这样:
torsten@pulsar:~$ python2.6 >>> import fcntl >>> help(fcntl.flock) flock(...) flock(fd, operation) Perform the lock operation op on file descriptor fd. See the Unix [...]
所以既然上游(指Python的开发者)没有使用这种功能,我就认为它是不存在的。:-)
好吧,我刚刚检查了当前的python3k源代码,情况还是这样。这个签名是在Python源代码中的 pydoc.py
文件里生成的,具体位置在这里:pydoc.py。相关的摘录从第1260行开始:
if inspect.isfunction(object): args, varargs, varkw, defaults = inspect.getargspec(object) ... else: argspec = '(...)'
inspect.isfunction 用来检查请求文档的对象是否是一个Python函数。但用C语言实现的函数被认为是内置函数,因此你总是会得到 name(...)
作为输出。