本文说一下如何把 python 翻译成如假包换的 c 源码, 然后编译成一个 python module。据官方博客称性能接近或超过 c。用到了 pypy —— python 史上最大的玩具。
先写个 python 源码文件, 待会编译出来:# hello.py 文件
def hello(a, b):
print a, b
return 'OK'
def target(drv, args):
return hello, [str, str]
先测试通过:>>> import hello
>>> result = hello.hello('hello', 'world!')
hello world!
>>> print result
OK
翻译成 c 并编译:
$ pypy/translator/goal/translate.py --batch hello.py
生成的目标在, /tmp/usession-dist-USERNAME/testing_1/testing_1.so 。
$ mv /tmp/usession-dist-USERNAME/testing_1/testing_1.so _hello.so
也可以写个自动化脚本编译完自动拷贝出去, 生成 testing_1.so 的目录位置在 driver.cbuider 对象里。可以参考 translate.py 写一个自己的编译平台 (哦, 一个玩笑)。
我们得到了 _hello.so 。里面有这么一个接口, 可以想见 python 调用起来很方便 (c 定义):
PyObject *pypy_g_pyfn_hello(PyObject *l_a_0, PyObject *l_b_0);
至少在 pypy-1.1.0 里是这样的, 以后就不一定了。
测试。先写个 _hello.so 的 python wrapper:# hello.py 文件, _hello.so 的 python wrapper
from ctypes import *
_hello = PyDLL('/PATH/TO/_hello.so')
_hello.RPython_StartupCode()
hello = _hello.pypy_g_pyfn_hello
hello.restype = py_object
hello.argtypes = [py_object, py_object]
测试通过:>>> import hello
>>> result = hello.hello('hello', 'world!')
hello world!
>>> print result
OK
这样, 我们就可以用 rpython 来写可编译执行的 python c module 了 (这是 c 后端, 据称在 cli 和 jvm 上能做的事情更多一些)。然后好处是, 你的模块可以同时兼容 python2、python3、pypy 了。进一步他是个 c lib (pypy 会导出 c 接口), 其实在任何语言平台上都是可以用的。
不过, 这显然不是标准的 pypy 应用, 事实上我们应该用 pypy 的 MixedModule 来写。不过可惜的是 MixedModule 编译到 python c module 的功能已经在 pypy-1.1.0 取消了。pypy 团队在事后就此事和广大 pypy 用户进行了讨论, 取消该功能是否是个好主意 (在已经取消的前提下)。官方觉得, 还是专注于把 jit 搞好吧, 其他炫的东西先放一放, 毕竟 jit 搞定了, 你就不再需要什么 python c module 了。总之, 你需要等很久, 无聊的话, 像这篇文章这样玩一下也不错 。
PS: 喜欢把 python 编译执行的朋友, 可以试一下 Cython 和 ShedSkin, 他们是专门为这个目标设计的。