为了提高效率,代码的性能部分可以用C写,再由Python调用。Python调用C扩展主要是一个参数传递的问题。以下是一个例子:

// spam.c
#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}

void print_str(const char *u, const char *v)
{
    printf("u = %s\nv = %s\n", u, v);
}

// wrap_spam.c
// Python.h must be included before any standard headers
#include <Python.h>

static PyObject *wrap_add(PyObject *self, PyObject *args)
{
    int a;
    int b;
    int res;

    // parse tuple using format string "ii" (two ints)
    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;

    res = add(a, b);
    return Py_BuildValue("i",res);
}

static PyObject *wrap_print_str(PyObject *self, PyObject *args)
{
    char *u;
    char *v;

    if (!PyArg_ParseTuple(args, "ss", &u, &v))
        return NULL;

    print_str(u, v);
    // increase ref counter
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *wrap_print_kw_str(PyObject *self, PyObject *args, PyObject *keywds)
{
    // v should be initialized since it's optional
    char *u;
    char *v = "default_v";

    // keyword list, set an element to '' if no keyword match
    char *kwlist[] = {"str1", "str2", NULL};

    // args after "|" are optional
    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|s", kwlist, &u, &v))
        return NULL;

    print_str(u, v);
    Py_INCREF(Py_None);
    return Py_None;
}

// List of methods.
//
// wrap_print_kw_str has type PyCFunctionWithKeywords,
// but the field in PyMethodDef is PyCFunction. It
// should be OK without this cast, but some warnings
// will be generated.
static PyMethodDef SpamMethods[] =
{
    {"add", wrap_add, METH_VARARGS, "add two numbers"},
    {"print_str", wrap_print_str, METH_VARARGS, "print two strings"},
    {"print_kw_str", (PyCFunction)wrap_print_kw_str, METH_VARARGS | METH_KEYWORDS, "print two strings, which can be specified by keyword args"},
    {NULL, NULL, 0, NULL}
};

// Init function. Name must be init+[module name]
void initspam()
{
    PyObject *m = Py_InitModule("spam", SpamMethods);
}

spam.c里定义了函数addprint_strwrap_spam.c是对这两个函数的包装以方便Python的调用。其中wrap_print_strwrap_print_kw_str都是对print_str的包装,但具有不同的参数接受形式。

由上例可以看出参数的转换主要是由这么几个函数完成的:

  • PyArg_ParseTuple
  • PyArg_ParseTupleAndKeywords
  • Py_BuildValue

具体说明参见http://docs.python.org/c-api/arg.html

为Python写C扩展主要有以下几个步骤:

  1. 包含头文件Python.h。如果还有其他头文件,必须放在Python.h后面。

  2. 封装函数。供Python调用的函数类型通常为以下二者之一:
    • PyCFunction类型,参见http://docs.python.org/c-api/structures.html#PyCFunction。简单说就是接受两个PyObject *类型的参数,返回一个PyObject *类型的值。
    • PyCFunctionWithKeywords类型。和PyCFunction类似,只是接受三个PyObject *类型的参数。
  3. 方法列表。包含了模块中可供调用的方法。参见http://docs.python.org/c-api/structures.html#PyMethodDef

  4. 初始化函数。注意函数名称必须是init+[module name]的形式。

  5. 编译链接。有两种方法:

    1. 直接使用gcc

      # Makefile
      
      spam.so:spam.c wrap_spam.c
          gcc -fpic -shared -o spam.so spam.c wrap_spam.c -I /usr/include/python2.7/
      
    2. 使用distutils。建立如下setup.py

      #!/usr/bin/python2
      
      from distutils.core import setup, Extension
      
      module1 = Extension('spam', sources = ['spam.c', 'wrap_spam.c'])
      
      setup (name = 'spam',
             version = '1.0',
             description = 'This is a spam package',
             ext_modules = [module1])
      
      

      然后运行 $ python2 setup.py build.

Links: