catalog
1. Python语言简介 2. Python模块 3. 嵌入式Python解析引擎: C++调用Python 4. Python 调用 C (base) 5. 扩展Python语法解析器功能: Python中调用C++ 6. Python 调用 C++ (Boost.Python) 7. python 调用 c++ (ctypes) 8. 脚本语言扩展性
1. Python语言简介
Python是一门简单易学且功能强大的编程语言。它拥有高效的高级数据结构,并且能够用简单而又高效的方式进行面向对象编程。Python优雅的语法和动态类型,再结合它的解释性,使其在大多数平台的许多领域成为编写脚本或开发应用程序的理想语言
1. 我们可以自由地从 Python官方点: http://www.python.org,以源代码或二进制形式获取 Python 解释器及其标准扩展库,并可以自由的分发。此站点同时也提供了大量的第三方 Python 模块、程序和工具,及其附加文档 2. 我们可以很容易的使用 C 或 C++(其他可以通过 C 调用的语言)为 Python 解释器扩展新函数和数据类型。Python 还可以被用作定制应用程序的一门扩展语言,这点和Lua是一致的
0x1: 使用 Python 解释器
1. Python 解释器通常被安装在目标机器的 /usr/local/bin/pythonXXX 目录下。将 /usr/local/bin 目录包含进 Unix shell 的搜索路径里,以确保可以通过输入: pythonXX 命令来启动,进入交互模式 2. 第二种启动 Python 解释器的方法是 python -c command [arg] ...,这种方法可以在 命令行 执行 Python 语句,类似于 shell 中的 -c 选项。由于 Python 语句通常会包含空格或其他特殊 shell 字符,一般建议将 命令 用单引号包裹起来 3. 有一些 Python 模块也可以当作脚本使用。你可以使用 python -m module [arg] ... 命令调用它们,这类似在命令行中键入完整的路径名执行 模块 源文件一样 4. 使用脚本文件时,经常会运行脚本然后进入交互模式。这也可以通过在脚本之前加上 -i 参数来实现
1. 参数传递
1. 调用解释器时,脚本名和附加参数传入一个名为 sys.argv 的字符串列表。你能够获取这个列表通过执行 import sys,列表的长度大于等于1 2. 没有给定脚本和参数时,它至少也有一个元素:sys.argv[0] 此时为空字符串 1) 脚本名指定为 ‘-‘ (表示标准输入)时,sys.argv[0] 被设定为 ‘-‘ 2) 使用 -c 指令 时,sys.argv[0] 被设定为 ‘-c‘ 3) 使用 -m 模块参数时,sys.argv[0] 被设定为指定模块的全名 4) -c 指令 或者 -m 模块 之后的参数不会被 Python 解释器的选项处理机制所截获,而是留在 sys.argv 中,供脚本命令操作
2. 交互模式
1. 从 tty 读取命令时,我们称解释器工作于 交互模式。这种模式下它根据主提示符来执行,主提示符通常标识为三个大于号(>>>) 2. 继续的部分被称为 从属提示符,由三个点标识(...)。在第一行之前,解释器打印欢迎信息、版本号和授权提示
3. 源程序编码
默认情况下,Python 源文件是 UTF-8 编码。在此编码下,全世界大多数语言的字符可以同时用在字符串、标识符和注释中 — 尽管 Python 标准库仅使用 ASCII 字符做为标识符,这只是任何可移植代码应该遵守的约定。如果要正确的显示所有的字符,你的编辑器必须能识别出文件是 UTF-8 编码,并且它使用的字体能支持文件中所有的字符
我们也可以为源文件指定不同的字符编码。为此,在 #! 行(首行)后插入至少一行特殊的注释行来定义源文件的编码
# -*- coding: encoding -*- # -*- coding: cp-1252 -*-
0x2: Python语法
1. 数字
1. 解释器表现得就像一个简单的计算器:可以向其录入一些表达式,它会给出返回值。表达式语法很直白:运算符 +,-,* 和 / 与其它语言一样(例如:Pascal 或 C);括号 (()) 用于分组 2. 整数(例如,2, 4, 20 )的类型是 int,带有小数部分的数字(例如,5.0, 1.6)的类型是 float 3. 除法(/)永远返回一个浮点数。如要使用 floor 除法 并且得到整数结果(丢掉任何小数部分),你可以使用 // 运算符;要计算余数你可以使用 % 4. 可以使用 ** 运算符计算幂乘方 5. 号( ‘=‘ )用于给变量赋值。赋值之后,在下一个提示符之前不会有任何结果显示 6. 变量在使用前必须 “定义”(赋值),否则会出错 7. 浮点数有完整的支持;整数和浮点数的混合计算中,整数会被转换为浮点数 8. 交互模式中,最近一个表达式的值赋给变量 _。这样我们就可以把它当作一个桌面计算器,很方便的用于连续计算,此变量对于用户是只读的。不要尝试给它赋值 —— 你只会创建一个独立的同名局部变量,它屏蔽了系统内置变量的魔术效果 9. 除了 int 和 float,Python 还支持其它数字类型,例如 Decimal 和 Fraction。Python 还内建支持 复数 ,使用后缀 j 或 J 表示虚数部分(例如,3+5j)
2. 字符串
相比数值,Python 也提供了可以通过几种不同方式表示的字符串。它们可以用单引号 (‘...‘) 或双引号 ("...") 标识。\ 可以用来转义引号
在交互式解释器中,输出的字符串会用引号引起来,特殊字符会用反斜杠转义。虽然可能和输入看上去不太一样,但是两个字符串是相等的
1. 如果字符串中只有单引号而没有双引号,就用双引号引用 2. 否则用单引号引用 3. print() 函数生成可读性更好的输出, 它会省去引号并且打印出转义后的特殊字符
如果你前面带有 \ 的字符被当作特殊字符,你可以使用 原始字符串,方法是在第一个引号前面加上一个 r
字符串文本能够分成多行。一种方法是使用三引号:"""...""" 或者 ‘‘‘...‘‘‘。行尾换行符会被自动包含到字符串中,但是可以在行尾加上 \ 来避免这个行为。下面的示例: 可以使用反斜杠为行结尾的连续字符串,它表示下一行在逻辑上是本行的后续内容:
字符串可以由 + 操作符连接(粘到一起),可以由 * 表示重复:
相邻的两个字符串文本自动连接在一起
它只用于两个字符串文本,不能用于字符串表达式
如果你想连接多个变量或者连接一个变量和一个字符串文本,使用 +,这个功能在你想切分很长的字符串的时候特别有用
字符串也可以被截取(检索)。类似于 C ,字符串的第一个字符索引为 0 。Python没有单独的字符类型;一个字符就是一个简单的长度为1的字符串(和PHP类似)
除了索引,还支持 切片。索引用于获得单个字符,切片 让你获得一个子字符串
注意,包含起始的字符,不包含末尾的字符。这使得 s[:i] + s[i:] 永远等于 s
切片的索引有非常有用的默认值;省略的第一个索引默认为零,省略的第二个索引默认为切片的字符串的大小(include to the end)
Python字符串不可以被更改 — 它们是 不可变的。因此,赋值给字符串索引的位置会导致错误
如果你需要一个不同的字符串,你应该创建一个新的
3. 列表
Python 有几个 复合 数据类型,用于表示其它的值。最通用的是 list (列表) ,它可以写作中括号之间的一列逗号分隔的值。列表的元素不必是同一类型(这点和Lua是类似的)
就像字符串(以及其它所有内建的 序列 类型)一样,列表可以被索引和切片:
所有的切片操作都会返回一个包含请求的元素的新列表。这意味着下面的切片操作返回列表一个新的(浅)拷贝副本
列表也支持连接这样的操作:
不像 不可变的 字符串,列表是 可变的,它允许修改元素
还可以使用 append() 方法在列表的末尾添加新的元素
也可以对切片赋值,此操作可以改变列表的尺寸,或清空它
允许嵌套列表(创建一个包含其它列表的列表),例如
4. 简单编程示例
1. 第一行包括了一个 多重赋值:变量 a 和 b 同时获得了新的值 0 和 1 最后一行又使用了一次,这点和Lua是类似的 2. 变量赋值前,右边首先完成计算。右边的表达式从左到右计算。 条件(这里是 b < 10 )为 true 时, while 循环执行。在 Python 中,类似于 C,任何非零整数都是 true;0 是 false 条件也可以是字符串或列表,实际上可以是任何序列
0x3: Python流程控制
1. IF语句
>>> x = int(input("Please enter an integer: ")) Please enter an integer: 42 >>> if x < 0: ... x = 0 ... print(‘Negative changed to zero‘) ... elif x == 0: ... print(‘Zero‘) ... elif x == 1: ... print(‘Single‘) ... else: ... print(‘More‘) ... More
2. FOR语句
Python 中的 for 语句和 C 或 Pascal 中的略有不同。通常的循环可能会依据一个等差数值步进过程(如 Pascal),或由用户来定义迭代步骤和中止条件(如 C ),Python 的 for 语句依据任意序列(链表或字符串)中的子项,按它们在序列中的顺序来进行迭代,相当于PHP中的foreach
在迭代过程中修改迭代序列不安全(只有在使用链表这样的可变序列时才会有这样的情况)。如果你想要修改你迭代的序列(例如,复制选择项),你可以迭代它的复本。使用切割标识就可以很方便的做到这一点
>>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words [‘defenestrate‘, ‘cat‘, ‘window‘, ‘defenestrate‘]
3. range()函数
如果你需要一个数值序列,内置函数 range() 会很方便,它生成一个等差级数链表:
可以指定一个不同的步进值(甚至是负数,有时这也被称为 "步长"),同时指定range的重复次数
range(5, 10) 5 through 9 range(0, 10, 3) 0, 3, 6, 9 range(-10, -100, -30) -10, -40, -70
需要迭代链表索引的话,如下所示结合使 用 range() 和 len()
4. break 和 continue 语句, 以及循环中的 else 子句
break 语句和 C 中的类似,用于跳出最近的一级 for 或 while 循环
5. pass 语句
pass 语句什么也不做。它用于那些语法上必须要有什么语句,但程序什么也不做的场合
>>> while True: ... pass # Busy-wait for keyboard interrupt (Ctrl+C) ...
0x4: 定义函数
def fib(n): # write Fibonacci series up to n """Print a Fibonacci series up to n.""" a, b = 0, 1 while a < n: print a, a, b = b, a+b print # Now call the function we just defined: fib(2000)
关于Python中的函数,需要明白以下几点
1. 关键字 def 引入了一个函数 定义。在其后必须跟有函数名和包括形式参数的圆括号。函数体语句从下一行开始,必须是缩进的 2. 函数体的第一行语句可以是可选的字符串文本,这个字符串是函数的文档字符串,或者称为 docstring 3. 函数 调用 会为函数局部变量生成一个新的符号表。确切的说,所有函数中的变量赋值都是将值存储在局部符号表。变量引用首先在局部符号表中查找,然后是包含函数的局部符号表,然后是全局符号表,最后是内置名字表。因此,全局变量不能在函数中直接赋值(除非用 global 语句命名),尽管他们可以被引用 //这点和PHP是类似的 4. 函数引用的实际参数在函数调用时引入局部符号表,因此,实参总是 传值调用(这里的 值 总是一个对象 引用 ,而不是该对象的值)(可以理解为指针传递)。一个函数被另一个函数调用时,一个新的局部符号表在调用过程中被创建。 5. 一个函数定义会在当前符号表内引入函数名。函数名指代的值(即函数体)有一个被 Python 解释器认定为 用户自定义函数 的类型。 这个值可以赋予其他的名字(即变量名),然后它也可以被当做函数使用。这可以作为通用的重命名机制
定义一个返回斐波那契数列数字列表的函数,而不是打印它
0x5: 编码风格
1. 使用 4 空格缩进,而非 TAB 2. 在小缩进(可以嵌套更深)和大缩进(更易读)之间,4空格是一个很好的折中。TAB 引发了一些混乱,最好弃用折行以确保其不会超过 79 个字符 3.使用空行分隔函数和类,以及函数中的大块代码 4. 可能的话,注释独占一行 5. 使用文档字符串 6. 把空格放到操作符两边,以及逗号后面,但是括号里侧不加空格:a = f(1, 2) + g(3, 4) 7. 统一函数和类命名 8. 推荐类名用 驼峰命名, 函数和方法名用 小写_和_下划线。总是用 self 作为方法的第一个参数 9. 不要使用花哨的编码,如果你的代码的目的是要在国际化环境。Python 的默认情况下,UTF-8,甚至普通的 ASCII 总是工作的最好 10. 同样,也不要使用非 ASCII 字符的标识符,除非是不同语种的会阅读或者维护代码
Relevant Link:
http://www.pythondoc.com/pythontutorial3/introduction.html http://www.pythondoc.com/pythontutorial3/controlflow.html
2. Python模块
如果你退出 Python 解释器并重新进入,你做的任何定义(变量和方法)都会丢失,为了满足这些需要,Python 提供了一个方法可以从文件中获取定义,这样的文件被称为 模块;模块中的定义可以 导入 到另一个模块或 主模块 中
模块是包括 Python 定义和声明的文件。文件名就是模块名加上 .py 后缀。模块的模块名(做为一个字符串)可以由全局变量 __name__ 得到
fibo.py
# Fibonacci numbers module def fib(n): # write Fibonacci series up to n a, b = 0, 1 while b < n: print b, a, b = b, a+b print def fib2(n): # return Fibonacci series up to n result = [] a, b = 0, 1 while b < n: result.append(b) a, b = b, a+b return result
可以使用import fibo命令导入这个模块,这样做不会直接把 fibo 中的函数导入当前的语义表;它只是引入了模块名 fibo。你可以通过模块名按如下方式访问这个函数:
如果打算频繁使用一个函数,你可以将它赋予一个本地变量
1. 除了包含函数定义外,模块也可以包含可执行语句。这些语句一般用来初始化模块。他们仅在 第一次 被导入的地方执行一次 2. 每个模块都有自己私有的符号表(符号表是模块独立的),被模块内所有的函数定义作为全局符号表使用。因此,模块的作者可以在模块内部使用全局变量,而无需担心它与某个用户的全局变量意外冲突 3. 模块可以导入其他的模块。一个(好的)习惯是将所有的 import 语句放在模块的开始(或者是脚本),这并非强制。被导入的模块名会放入当前模块的全局符号表中 4. import 语句的一个变体直接从被导入的模块中导入命名到本模块的语义表中。例如: /* >>> from fibo import fib, fib2 >>> fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 这样不会从局域语义表中导入模块名(如上所示,fibo没有定义) */ 5. 甚至有种方式可以导入模块中的所有定义 /* >>> from fibo import * >>> fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 这样可以导入所有除了以下划线( _ )开头的命名 */
0x1: 编译的Python文件
为了加快加载模块的速度,Python 会在 __pycache__ 目录下以 module.version.pyc 名字缓存每个模块编译后的版本,这里的版本编制了编译后文件的格式。它通常会包含 Python 的版本号
Python 会检查源文件与编译版的修改日期以确定它是否过期并需要重新编译。这是完全自动化的过程。同时,编译后的模块是跨平台的,所以同一个库可以在不同架构的系统之间共享
Python 不检查在两个不同环境中的缓存。首先,它会永远重新编译而且不会存储直接从命令行加载的模块。其次,如果没有源模块它不会检查缓存。若要支持没有源文件(只有编译版)的发布,编译后的模块必须在源目录下,并且必须没有源文件的模块
0x2: 标准模块
Python 带有一个标准模块库,并发布有独立的文档。有一些模块内置于解释器之中,这些操作的访问接口不是语言内核的一部分,但是已经内置于解释器了。这既是为了提高效率,也是为了给系统调用等操作系统原生访问提供接口。这类模块集合是一个依赖于底层平台的配置选项。例如,winreg 模块只提供在 Windows 系统上才有。有一个具体的模块值得注意: sys这个模块内置于所有的 Python 解释器
0x3: dir() 函数
内置函数 dir() 用于按模块名搜索模块定义,它返回一个字符串类型的存储列表
该列表列出了所有类型的名称:变量,模块,函数,等等,dir() 不会列出内置函数和变量名。如果你想列出这些内容,它们在标准模块 builtins 中定义
0x4: 包
包通常是使用用"圆点模块名"的结构化模块命名空间。例如,名为 A.B 的模块表示了名为 A 的包中名为 B 的子模块。正如同用模块来保存不同的模块架构可以避免全局变量之间的相互冲突,使用圆点模块名保存像 NumPy 或 Python Imaging Library 之类的不同类库架构可以避免模块之间的命名冲突(类似于Java对包的圆点命名管理)
假设现在想要设计一个模块集(一个"包")来统一处理声音文件和声音数据。存在几种不同的声音格式(通常由它们的扩展名来标识,例如:.wav, .aiff,.au),于是,为了在不同类型的文件格式之间转换,你需要维护一个不断增长的包集合。可能你还想要对声音数据做很多不同的操作(例如混音,添加回声,应用平衡 功能,创建一个人造效果),所以你要加入一个无限流模块来执行这些操作。你的包可能会是这个样子(通过分级的文件体系来进行分组)
sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for file format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ...
当导入这个包时,Python 通过 sys.path 搜索路径查找包含这个包的子目录
Relevant Link:
http://www.pythondoc.com/pythontutorial3/modules.html
3. 嵌入式Python解析引擎: C++调用Python
在C/C++中嵌入Python,可以使用Python提供的强大功能,通过嵌入Python可以替代动态链接库形式的接口,这样可以方便地根据需要修改脚本代码,而不用重新编译链接二进制的动态链接库
C++调用Python有两种方式
1. 第一种方式: 通过找到Python模块,类,方法,构造参数来调用 2. 第二中方式: 就是通过构造出一个Python的脚本,用python引擎来执行
第一种方式可能更为优雅,符合大多数的反射调用的特点(如C#的反射机制、C#调用Com+、C#调用javascript脚本等)
这里存在的一个问题是,两种语言互相调用的时候,需要做数据结构(如基本类型、字符串、整数类型等、以及自定义的类等类型)间的转换,共享内存中的一个对象。比如,如何将C++的对象实例传入Python中,并在Python中使用。C++和Python并不在一个进程中,因此可以使用Boost的shared_ptr来实现。Python调用C++,换句话说就是需要把C++封装成Python可以"理解"的类型。同理C++去调用Python脚本也是一样的
0x1: 代码示例
test.c
/** @file test.c gcc -Wall -O2 -o test test.c -I/usr/include/python2.6 -L/usr/lib64/python2.6 -lpython2.6 -Wl,-R/usr/lib64/python2.6 */ #include <stdio.h> #include <string.h> #include <Python.h> int main(int argc, char *argv[]) { PyObject *pmod = NULL; PyObject *pstr = NULL; PyObject *pfunc = NULL; PyObject *pargs = NULL; char *cstr = NULL; /* 初始化解释器 */ Py_Initialize(); /* 构建一个元组保存参数, PyEval_CallObject的第二参数是一个元组 */ pargs = Py_BuildValue("(s)", argv[1]); /* 添加Python路径, 包括当前路径, 否则不能导入当前路径下的模块 */ PyRun_SimpleString("import sys;sys.path.append(‘.‘)"); /* 导入模块名称, 通常为调用的Python脚本程序名 */ pmod = PyImport_ImportModule("testpy"); if (pmod == NULL) { printf("import module failed!\n"); return -1; } /* 获得导入模块的函数属性 */ pfunc = PyObject_GetAttrString(pmod, "testpy"); if (pfunc == NULL) { printf("No such attribute!\n"); return -1; } /* 调用导入模块的入口函数, 获取返回结果 */ pstr = PyEval_CallObject(pfunc, pargs); if (pstr == NULL) { printf("callobject failed!\n"); return -1; } /* 分析返回的结果 */ PyArg_Parse(pstr, "s", &cstr); printf("%s\n", cstr); /* 关闭解释器 */ Py_Finalize(); return 0; }
在C程序中嵌入的Python脚本程序示例,testpy.py:
#!/usr/local/bin/python import sys def testpy(name): if not name: return ‘Valid Arguments‘ str = "hello, " + name return str
Relevant Link:
http://www.qmailer.net/archives/103.html
4. Python 调用 C (base)
Python是解释性语言, 底层就是用C实现的
0x1: 代码示例
/** @file wrapper.c gcc -fPIC wrapper.c -o wrapper.so -shared -I/usr/include/python2.6 -I/usr/lib64/python2.6/config */ #include <Python.h> int fact(int n) { if (n <= 1) return 1; else return n * fact(n - 1); } PyObject* wrap_fact(PyObject* self, PyObject* args) { int n, result; if (! PyArg_ParseTuple(args, "i:fact", &n)) return NULL; result = fact(n); return Py_BuildValue("i", result); } static PyMethodDef wrapperMethods[] = { {"fact", wrap_fact, METH_VARARGS, "Caculate N!"}, {NULL, NULL} }; void initwrapper() { PyObject* m; m = Py_InitModule("wrapper", wrapperMethods); }
进入python, 可以如下使用
Relevant Link:
http://blog.csdn.net/fxjtoday/article/details/6059874
5. 扩展Python语法解析器功能: Python中调用C++
在python中调用C++类成员函数, 如下调用TestFact类中的fact函数
wrapper.cpp
/* g++ -fPIC wrapper.cpp -o example.so -shared -I/usr/include/python2.6 -I/usr/lib64/python2.6/config wrapper.cpp */ #include <Python.h> class TestFact { public: TestFact(){}; ~TestFact(){}; int fact(int n); }; int TestFact::fact(int n) { if (n <= 1) return 1; else return n * (n - 1); } int fact(int n) { TestFact t; return t.fact(n); } PyObject* wrap_fact(PyObject* self, PyObject* args) { int n, result; if (! PyArg_ParseTuple(args, "i:fact", &n)) return NULL; result = fact(n); return Py_BuildValue("i", result); } static PyMethodDef exampleMethods[] = { {"fact", wrap_fact, METH_VARARGS, "Caculate N!"}, {NULL, NULL} }; extern "C" //不加会导致找不到initexample void initexample() { PyObject* m; m = Py_InitModule("example", exampleMethods); }
然后在有此so库的目录, 进入python, 可以如下使用
Relevant Link:
http://www.oschina.net/question/234345_48628 http://www.yexiwei.com/2013/06/27/python%E8%B0%83%E7%94%A8cc%E5%87%BD%E6%95%B0/
6. Python 调用 C++ (Boost.Python)
Boost库是非常强大的库, 其中的python库可以用来封装c++被python调用, 功能比较强大, 不但可以封装函数还能封装类, 类成员
Relevant Link:
http://blog.csdn.net/fxjtoday/article/details/6059874
7. python 调用 c++ (ctypes)
ctypes is an advanced ffi (Foreign Function Interface) package for Python 2.3 and higher. In Python 2.5 it is already included.
ctypes allows to call functions in dlls/shared libraries and has extensive facilities to create, access and manipulate simple and complicated C data types in Python - in other words: wrap libraries in pure Python. It is even possible to implement C callback functions in pure Python.
Relevant Link:
http://blog.csdn.net/fxjtoday/article/details/6059874
8. 脚本语言扩展性
需要明白的是,无论是Lua中调用C++扩展API,还是Python中调用C++ API,本质上都是通过外部的C++实现逻辑功能代码,通过联合编译或者so/dll的方式导入到脚本引擎中,只是区别在于python已经有大量现成的代码库和so扩展库,而Lua在这方面还很原始,很多时候还需要使用联合编译的方式实现
0x1: SWIG
有一个外部工具叫SWIG,是Simplified Wrapper and Interface Generator 的缩写。其作者为David Beazley。这个工具可以根据特别注释过的C/C++头文件生成能给Python,Tcl 和Perl 使用的包装代码。使用SWIG 可以省去你写前面所说的样板代码的时间。你只要关心怎么用C/C++解决你的实际问题就好了。你所要做的就是按SWIG 的格式编写文件,其余的就都由SWIG 来完成
0x2: Pyrex
0x3: Psyco
Psyco 的理念与其它的方法截然不同。与其改成C的代码,为何不让你已有的Python代码运行的更快一些呢?Psyco 是一个just-in-time(JIT)编译器,它能在运行时自动把字节码转为本地代码运行。所以,你只要(在运行时)导入Psyco 模块,然后告诉它要开始优化代码就可以了。而不用修改自己的代码
Relevant Link:
http://swig.org http://cosc.canterbury.ac.nz/~greg/python/Pyrex http://blog.csdn.net/pipisorry/article/details/49532341
Copyright (c) 2015 LittleHann All rights reserved