pythondd_工程脚本插件方案 - c集成Python基础篇

工程脚本插件方案 - c集成Python基础篇

序: 为何要集成脚本,怎样在工程中集成Python脚本。

在做比较大型的工程时,1般都会分核心层和业务层。核心层要求实现高效和稳定的基础功能,并提供调用接口供业务层调用的1种标准的框架划分。在实际中根据需求会拆分的更细。

外部的表现情势就是1个核心动态库,带着1堆业务业务动态库。通过1个调度程序把这些链接起来,外加1堆配置文件,就构成1个完成的项目。

这类模式在1个团队开发中,工作职责比较容易划分。制定API接口后,开发工作基本可以并行实现,包括后期的功能测试(白盒、黑盒)。不管工程使用甚么语言,基本都是如此。

c语言无疑是很强大而又灵活的,但是开发比较复杂,开发工期比较长。全部使用c/c++ 进行开发的话,编译调试整合发布,就需要大量时间,包括运营保护的话,呵呵~ 。全部产品的生命周期需要投入大量的人力来保持这个产品。特别是对做定制的终究用户的话,那便可能就是1场旷日持久战。

刚才说的1般工程都会包括2方面,核心和扩大。是公司级产品的话,1定会出于盈利问题,会对核心做保密(商业机密)。扩大是在核心基础上实现的,很大程度上保密等级就没那末高,乃至可以是开放式的。方便有1定能力的用户直接扩大。这是最理想的1种情况。

如果业务层还是使用c/c++的话,估计用户没有几个有能力或说不太愿意去做扩大。只能公司团队进行保护和扩大。固然开源项目除外,活跃的开源项目还是有很多大侠们愿意去扩大的。当下降扩大的难度,不但可以缩短开发周期下降本钱,下降运营保护本钱。如果产品够优秀,还能吸引有1定能力的客户来帮忙做扩大。扩大简单方便,用户自己都能扩大,项目运营本钱一定下降。这就皆大欢乐。

脚本是1个非常方便的东西,不需要编译直接运行就可以看到结果。不用斟酌大量系统相干的开发技能,更贴近实际业务的描写,修正问题不需要重新编译发布,保护非常方便。能大大提供生产效力,这就是我们所须要的。现在流行的脚本语言有很多Ruby、Perl、Python、Lua、Javascript等等。。。,反正很多。

脚本语言比较:

1、python: 简单易学,有大量扩大可使用。

2、Ruby: 魔幻型纯面向对象语言,非常灵活,学习相对其他语言有1定难度。

3、Lua: 超轻量级,性能高效。很多游戏使用Lua提供扩大。

使用那种脚本作为扩大,要看实际项目和现有资源的情况而定。本文只介绍python的集成。

空话这么多,主要的目的就下面几个。

扩大目的:

1、高可配性。 解决1些简单配置不能实现的组织、回调功能,避免改动重编重发布。

2、为了使用已有的库。 如原来你有很多积累,写了1些合适工程开发的库。

3、优化程序提升性能。 对脚本程序使用库的1种情况。

集成: 就是通过简单、直接和快速的在不同语言直接调度切换控制,属于无缝连接。就像使用同1种语言在不同的动态库之间调度。而不是那种使用套接字和管道等的间接调度。

混合开发中,不管python或C都可以作为“上层”。因此两方面都要提供入口提供对方调度。这个其实和正常的同1语言编写的插件模式是1致的。核心提供接口和注册入口,扩大注册入口并调用接口。

嵌入接口: C程序中运行Python的代码

扩大接口: Python程序中运行C的代码库

测试环境:

window 7

vs2015

python 3.5

1、准备环境

使用的Python是3.5版本的。

首先肯定是须要个python的运行环境,可以直接从官网python.org下载Python3.x 进行安装。建议直接下源代码编译,由于里面有很多代码可以参考。

windows配置

python安装路径以下为例d:\python,在系统的用户环境变量中添加。

1、 增加python搜索路径,方便代码运行调试

path=d:\python;d:\python\pcbuild\win32

2、 增加python环境路径。加载模块时默许会从配置路径中搜索。

PYTHONPATH=.;d:\python\lib;d:\python\pcbuild\win32;D:\Python\Lib\site-packages;d:\python

3、 增加编译路径。

PyInc=d:\python\include;d:\python\pc

PyLib=d:\python\pcbuild\win32

方便VS搜索路径配置。在工程中,只需援用配置变量路径,可以直接使用(PyInc)和(PyLib)。

提示: 由因而自己编译的,1些环境参数没有须要自己加。注意大小写

2.1、嵌入Python第1个简单工程

创建测试工程

在VS中创建1个空的VC++控制台程序。在工程选择的搜索目录中加入$(PyInc)。

选择菜单:Project->Properties...

在VC++ Directories分类的Include Directories中加入先前定义的环境变量$(PyInc)。确保Python的头文件能搜索到。

使用环境变量的1个好处是,方便不同机器不同环境的切换,不需要修改工程配置。

提示: 可以不建工程,直接使用makefile方式进行编译。

创建script.py脚本

在工程中创建1个新文件,直接改名为script.py,复制下面内容。

注意: 编码设置为UTF⑻

"""

在C中调用Python模块运行。

调用时须要把文件放在程序当前运行目录(保证在搜索目录中)。

www.moguf.com 2016-05⑵8

"""

message = 'hello life...'

def transform(input):

input = input.replace('life', 'Python')

return input.upper()

在Python环境中运行这个脚本,可以得到下面结果。可以看到打印出 hello life... 和转换后的字符串 HELLO PYTHON...

Microsoft Windows [版本 6.1.7601]

版权所有 (c) 2009 Microsoft Corporation。保存所有权利。

C:\Users\CrystalIce>cd /d D:\Dev\MySimple\pythoninc\embedsimple

D:\Dev\MySimple\pythoninc\embedsimple>python

Running Release|Win32 interpreter...

Python 3.5.0 (default, Nov 4 2015, 21:58:28) [MSC v.1900 32 bit (Intel)] on win32

Type "help", "copyright", "credits" or "license" for more information.

>>>import script

>>>print(script.message)

hello life...

>>>x = script.message

>>>print(script.transform(x))

HELLO PYTHON...

>>>

创建hello.c

在工程中添加1个新文件,命名为hello.c。复制下面内容。

//

// C code runs Python code in this module in embedded mode.

// print hello string

//

// www.moguf.com 2016-05⑵8

//

#include

int main()

{

Py_Initialize();

PyRun_SimpleString("print('run python ...')");

PyRun_SimpleString("import script");

PyRun_SimpleString("print(script.message)");

PyRun_SimpleString("x = script.message");

PyRun_SimpleString("print(script.transform(x))");

Py_Finalize();

}

运行程序

在程序最后下个断点,方便查看运行结果。直接运行程序可以看到下面结果。

run python ...

hello life...

HELLO PYTHON...

基本调用流程解析

使用C程序运行Python脚本代码,可以通过使用Python字符串,调用Python对象或模板之类的所有操作。

流程:

1、初始化Python解析器

2、履行Python代码,字符串,对象或模块。

3、关闭Python解析器。

上面代码嵌入进程很容易。但在实际使用中想要更好的整合,须要了解提供的API和不同语言之间的转换。

Python嵌入C的基础API

下面几个基础API,在C中能很容易的履行Python脚本中的代码。包括字典、数组和对象。固然想要更好的混合交互须要熟习所有的API。

C API 调用

Python 对应

PyImport_ImportModel

import module

PyImport_ReloadModule

reload(module)

PyImport_GetModuleDict

module.__dict__

PyDict_GetItemString

dict[key]

PyDict_SetItemString

dict[key] = value

PyDict_New

dict = {}

PyObject_GetAttrString

getattr(obj, attr)

PyObject_SetAttrString

setattr(obj, attr, val)

PyObject_CallObject

funcobj(*argstuple)

PyEval_CallObject

funcobj(*argstuple)

PyRun_String

eval(exprstr) , exec(stmtstr)

PyRun_File

exec(open(filename().read())

建议: 去官网下载1个手册,方便查看API。https://docs.python.org/3/download.html

2.2、使用C扩大Python

2.1的内容只是通过C程序调用Python脚本,要让Python脚本能调用C代码,就须要扩大。用C扩大Python功能那就简单很多。有很多实例可以参考。Python源代码就是宝库。

创建扩大工程 hello

在VS中创建1个空的动态库hello工程(先不要改名)。在工程配置中增加搜索路径。

在VS生成时有些特殊。生成的后缀选择.pyd主要是为避免和系统.dll产生冲突。

在工程选项界面中设置工程输出名称为$(ProjectName)_d,输出扩大名称为.pyd。

并在Linker页面Input组中设置库依赖为python35_d.lib

不同的编译模式的设置:

Release 下使用的依赖库为pythonXY.lib

Debug 下使用依赖库为 pythonXY_d.lib

注意:

debug 模式生成应为 hello_d.pyd

release 模式生成应为 hello.pyd

创建hello.c扩大代码

在工程中新建hello.c文件,复制下面内容。

//

// A simple C extension module for python, called "hello"

//

// www.moguf.com 2016-05⑵8

//

#include

#include

//

// module functions

//

static PyObject * // returns object

message(PyObject *self, PyObject *args)

{

char *fromPython, result[1024];

if (!PyArg_Parse(args, "(s)", &fromPython)) // convert Python -> C

return NULL; // exception null = raise

else {

strcpy(result, "Hello , "); // build up C string

strcat(result, fromPython); // add passed Python string

return Py_BuildValue("s", result); // convert C -> Python

}

}

//

// registration methods table

static PyMethodDef hello_methods[] = {

{ "message", message, METH_VARARGS, "func doc" }, // format: name, &func, fmt, doc

{ NULL, NULL, 0, NULL } // end

};

// module definition structure

static struct PyModuleDef hellomodule = {

PyModuleDef_HEAD_INIT,

"hello", // module name

"mod doc", // module documentation,

-1,

hello_methods // methods table

};

//

// module initializer

PyMODINIT_FUNC

PyInit_hello()

{

return PyModule_Create(&hellomodule);

}

上面的代码主要分为4块。

第1块: 模块功能实现函数

第2块: 注册功能函数

第3块: 定义模块申明

第4块: 初始化模块。动态加载就不需要这块内容,集成时会使用动态加载。

通过上述定义为Python脚本调用提供访问入口,这就是通常所说的胶水代码。具体定义直接看代码注释,就不啰嗦了。

这里须要注意的是定义中的名称hello。在第3块模型注册的时候是名称为hello、第4块中函数的初始化名称PyInit_hello()。在python3中的名称规定比较严格,初始化函数名称格式为PyInit_xxx, xxx为注册的模块名称。

即对Python扩大工程中的工程名称,注册名称和初始化名称须要保持1致。

编译测试运行

编译hello工程(debug版本),在Python调试版本下运行。python调试环境使用python_d命令进入。

可以看到下面结果,就说明OK了

Microsoft Windows [版本 6.1.7601]

版权所有 (c) 2009 Microsoft Corporation。保存所有权利。

C:\Users\CrystalIce>cd /d D:\Dev\MySimple\pythoninc\Debug

D:\Dev\MySimple\pythoninc\Debug>dir

驱动器 D 中的卷是 Docs

卷的序列号是 0002⑵203

D:\Dev\MySimple\pythoninc\Debug 的目录

2016-05⑵8 23:10

2016-05⑵8 23:10

2016-05⑵8 23:10 639 hello_d.exp

2016-05⑵8 23:10 247,520 hello_d.ilk

2016-05⑵8 23:10 1,718 hello_d.lib

2016-05⑵8 23:10 503,808 hello_d.pdb

2016-05⑵8 23:10 35,840 hello_d.pyd

5 个文件 789,525 字节

2 个目录 27,394,551,808 可用字节

D:\Dev\MySimple\pythoninc\Debug>python_d

Python 3.5.0 (default, Nov 4 2015, 21:57:44) [MSC v.1900 32 bit (Intel)] on win32

Type "help", "copyright", "credits" or "license" for more information.

>>> import hello

>>> print(hello.message('C'))

Hello , C

>>> print(hello.message('module ' + hello.__file__))

Hello , module D:\Dev\MySimple\pythoninc\Debug\hello_d.pyd

>>>

如果在运行调试中出现下面情况,是Python找不到hello模块致使的。

>>> import hello

Traceback (most recent call last):

File "", line 1, in

ImportError: No module named 'hello'

大致缘由:

编译的模块名称有问题,加载不到模块。Python在debug环境下会调用 xxx_d.pyd,release下会调用 xxx.pyd。

创建hellouse.py 调用hello扩大模块

在工程中新建1个hellouse.py,用于调度hello扩大模块。

注意: 编码设置为UTF⑻

内容以下:

"""

import and use a C extension library module

www.moguf.com 2016-05⑵8

"""

import hello

print(hello.message('C'))

print(hello.message('module ' + hello.__file__))

把这个脚本复制到hello_d.pyd扩大库所在目录,并履行 。可以看到和刚才测试输出的结果是1致的。

D:\Dev\MySimple\pythoninc\Debug>python_d hellouse.py

Hello , C

Hello , module D:\Dev\MySimple\pythoninc\Debug\hello_d.pyd

相干编译问题:

如果自己建的工程编译或调试,老出现状态。可以直接使用Python提供的PC\example_ntVS示例工程作为参考。

2.3、集成Python,实现双工

先前的的两个示例都是单方面调用,c调用Python 和 Python调用c的扩大模型。并没有交互。在实际工程中不太可能有这类情况,1定是相互交叉调用。

创建duplex工程

在VS中创建1个空的控制台程序,并设置Python代码搜索路径,参照2.1。

创建duplex.c 文件

这个原文件包括了脚本调用和胶水代码的实现。和2.1、2.2的内容基本1致。其中主要的差异在Python模块的注册上

PyImport_AppendInittab("hello_api", &PyInit_hello_api);

实际对外注册的模块在程序启动时履行,并没有作导出。

文件内容以下。

//

// c API module, test c embedding and extending

//

// www.moguf.com 2016-05⑵9

//

#include

#include

void helloWorld(char *param)

{

if (param)

printf("It's c, hello %s", param);

else

printf("It's c, hello ");

}

static PyObject *

message(PyObject *self, PyObject *args)

{

char *fromPython;

if (!PyArg_Parse(args, "(s)", &fromPython))

helloWorld(NULL);

else

helloWorld(fromPython);

return Py_BuildValue("");

}

static PyMethodDef hello_methods[] = {

{ "message", message, METH_VARARGS, "func doc" },

{ NULL, NULL, 0, NULL } // end

};

static struct PyModuleDef hello_api = {

PyModuleDef_HEAD_INIT,

"hello_api",

"mod doc",

-1,

hello_methods

};

static PyObject*

PyInit_hello_api(void)

{

return PyModule_Create(&hello_api);

}

int main(int argc, char** argv)

{

PyObject* module;

PyObject* func;

// add c api to modules

PyImport_AppendInittab("hello_api", &PyInit_hello_api);

Py_Initialize();

if (!Py_IsInitialized()) {

PyErr_Print();

printf("Couldn't init python");

return -1;

}

module = PyImport_ImportModule("plugins");

if (module) {

func = PyObject_GetAttrString(module, "helloWorld");

if (func && PyCallable_Check(func)) {

PyObject* pArgs = NULL;

PyObject* pReturnVal = PyObject_CallObject(func, pArgs);

}

else {

PyErr_Print();

printf("error: no func\n");

}

Py_XDECREF(func);

Py_DECREF(module);

}

else {

PyErr_Print();

printf("err: no module");

}

Py_Finalize();

return 0;

}

创建脚本plugins.py

内容以下

"""

Module to test c embedding and extending

www.moguf.com 2016-05⑵9

"""

import hello_api

def helloWorld():

print("it's Python, Hello C")

hello_api.message('python')

return

运行测试

在 main函数结束的位置设置断点,这用方便查看结果。运行程序。

it's Python, Hello C

It's c, hello python

可以看到,第1行打印是由Python脚本实现输出,第2行是由python调用程序的API实现打印输出。

3、后续

通过上述简单的3个实例实现了c语言和Python脚本的集成。简单、直接和快速的在不同语言直接调度切换控制。

由于Python开始时本身就是基于C写的,所有对c的支持是非常好的。能在c/c++中很方便的进行集成。不过要想更好的实现脚本和C进行交互,那就须要熟习并使用提供的API。

后续将会使用Python脚本作为插件扩大1种模式,在实际工程中实现业务的1些方案。

相干参考:

1、官方帮助 https://docs.python.org/3/

2、源代码:https://github.com/cmacro/simple/tree/master/pythoninc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值