如何嵌入pyrex
简介
对于大多数Py程序员来说,pyrex已经是一个实现Python/C包装的成功解决方案,他消除了所有Python/C之间的杂七杂八的问题。
但是大部分的pyrex使用都是集中于构建扩展类型和模块,使得他们可以象一般的Python模块一样的使用。
这个小教程会引导你制作单独的可执行程序,其实是将pyrex嵌入了Python解释器来运行。
嵌入Python?怎么做到的?为什么这么做?
现代,操作系统、编程语言和开发工具都成为了一种信仰,Python也无法逃脱。
如果你尝试嵌入,那么会有一大堆Python程序员嘲笑你,他们更推崇扩展,而不是嵌入。
在大多数情况下,他们是对的。而且大多数时候,扩展Python也比嵌入Python到C程序更有意义。
但是仍然有一些情况嵌入是更方便的,例如:
- 一个程序需要711权限(u+rwx,go-rw+x)
- 一个程序需要在ps和top命令中按照名称显示,而不是python
- 懒得区别用户的Python版本
- 适合你的其他理由
这个教程会谈到哪些东西?
这个文档中会介绍使用pyrex构建单独的可执行程序的过程,或者说看起来像一个可执行程序,当然运行时会动态的链接libpython2.x.so文件。
处方
创建一个简单的.pyx文件
如下代码是完整的pyrex源码,可以直接编译成可执行文件:
"""
实验通过pyrex嵌入Python
"""
#从C头文件中获取我们需要的功能
cdef extern from "stdio.h":
int printf(char* format,...)
cdef extern from "Python.h":
#嵌入函数
void Py_Initialize()
void Py_Finalize()
void PySys_SetArgv(int argc, char** argv)
#声明其他的Python/C接口函数
void Py_INCREF(object o)
void Py_DECREF(object o)
object PyString_FromStringAndSize(char *, int)
#注意:必须声明函数原型 'init<mymodulename>()' 而mymodulename就是这段
#代码的文件名,比如本例就叫做testpyx.pyx
cdef public void inittestpyx()
#这里可以定义一系列的Python扩展类型、类、方法等
cdef class Testclass:
cdef public int someint
cdef public char* somestring def __init__(self):
self.someint=43
self.somestring="this is a string"
def hello(self):
print "Hello, this is an instance of %s"%self.__class__.__name__
#现在可以声明C的main()函数了
cdef public int main(int argc, char** argv):
Py_Initialize()
PySys_SetArgv(argc,argv)
printf("initialising testpyx\n")
inittestpyx()
#初始化完成,可以做Python的功能了
printf("testmain: instantiating Testclass\n")
testobj=Testclass()
printf("testmain: created testobj\n")
print "testobj.someint=%s"%testobj.someint
print "testobj.somestring=%s"%testobj.somestring
print "calling testobj.hello()"
testobj.hello()
#离开程序之前释放资源
print "cleaning up"
Py_Finalize()
编写Makefile
如下的Makefile对我工作的很好,希望对你有用:
#创建单独的Pyrex程序
PYVERSION=2.3
PYPREFIX=/usr
INCLUDES=-i$(PYPREFIX)/include/python$(PYVERSION)testpyx: testpyx.o
gcc -o $@ $^ -lpython$(PYVERSION)
testpyx.o: testpyx.c
gcc -c $^ $(INCLUDES)
testpyx.c: testpyx.pyx
pyrexc testpyx.pyx
总结
想要做可以单独工作的pyrex可执行程序,必须要:
-
导入必须的几个API: Py_Initialize() 、 Py_Finalize() 、 PySys_SetArgv() ,还有就是你需要调用的其他函数
-
必须明确的声明pyrex生成的模块初始化函数的C原型,如本例的’inittestpyx’。
-
创建一个C可以调用的’main()’函数,需要注意:
- 初始化Python解释器
- 调用pyrex生成的模块初始化函数
- 随程序需要随便写Python语句
- 停止Python解释器
-
创建一个Makefile需要:
- 明确的编译.pyx到.c文件,使用单独的命令 pyrexc
- 编译生成的.c文件到.o或.obj文件,确保提供正确的include路径
- 链接结果到最终的可执行文件,并确保它可以找到Python运行时库