蒋涛ID:joliny
98832次访问,排名890好友53人,关注者73
绘画,听音乐,读书,乒乓球,乐器,电脑技术学习。
joliny的文章
原创 68 篇
翻译 0 篇
转载 570 篇
评论 38 篇
蒋涛的公告

个人简介
网名:夜语无声(joliny)
QQ:154238669
Skype:joliny
MSN:jbakwd@hotmail.com

来自:西安

今日天气

最近评论
liangitjob:验证码刷新:
<script>
function refresh(e){
e.src="/工程名/validateCodeServlet"+new Date();
}
</script>

<img src="/工程名/validateCodeServlet" onclick="refresh……
z60010232:<%@ page language="java" pageEncoding="GBK"%>

<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-html" ……
l:你浪费别人的时间!
laurancechen:顶一个
guest:good
文章分类
收藏
    相册
    梦幻天空
    blog
    IBM developerWorks 中国
    相濡以沫
    forum
    java
    Ajax中国
    cgi教程
    IBM中文
    IocBlog
    ItPub
    J2ME开发
    JavaEye
    javascript
    JavaWorld
    MemCached 中文站
    minuteman的BLOG
    redsaga
    smarty
    中国it实验室
    中文JAVA技术网
    开放技术文档查询系统
    数据结构
    最实用的Linux博客
    python
    Django Book
    limodou学习笔记
    PyScripter
    sleepless
    thinkhole.org
    啄木鸟 Pythonic 开源社区
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    转载 使用Delphi 编写Python Extension 收藏

    新一篇: windows 最大物理内存 | 旧一篇: python Ini文件读写

    使用Delphi 编写Python Extension 作者:1000copy 摘要: 在互联网公共可访问领域内,关于Python/C interface的介绍,手册都是比较多的。Py直接支持C编写扩展,对于Delphi程序员,P4D是一个很好的选择。 不幸的是,通过P4D[2]编写PyExtention,并没有一个很好的入门文档,本文试图填写这个空白。 本文风格完全模仿Writing Python Extensions[1],希望以例子为本,让大家很快的进入状态。 1. 引言: 本文假设你:     * 懂得Python     * 懂得Delphi     * 想要通过P4D编写Python Extension     * 已经安装了Delphi7,P4D,Python2.4以上。 2. 第一个Python Extension    以下的例子是可以直接使用的,只要拷贝如下代码,存放到ExAdd.dpr,直接用Delphi编译,就可以成为一个Python Extension 。    我们可以首先看到效果,然后在分析程序。 2.1 最小的例子     {文件名 ExAdd.dpr}     library ExAdd;     uses SysUtils,Classes,PythonEngine;     {$E pyd}     var      FModule : TPythonModule;      FEngine:TPythonEngine ;     function Add( Self, Args : PPyObject ) : PPyObject; far; cdecl;     var       a, b : Integer;     begin       with GetPythonEngine do         begin           if PyArg_ParseTuple( args, 'ii:Add', [@a, @b] )  0 then             begin               Result := PyInt_FromLong( a + b );             end           else             Result := nil;         end;     end;                    procedure initExAdd; cdecl;     begin       FEngine := TPythonEngine.Create(nil);       FModule := TPythonModule.Create(nil);       FModule.Engine := FEngine;       FModule.ModuleName := 'ExAdd';       FModule.AddMethod( 'exadd', @Add, 'exadd(a,b) -> a+b ' );       FEngine.LoadDll;     end;     exports       initExAdd;     var         OldExitProc: pointer;     procedure MyExitProc;     begin       FModule.Free;       FEngine.Free;       ExitProc := OldExitProc;     end;     begin       OldExitProc := ExitProc;       ExitProc := @MyExitProc;     end.     // 测试代码     //from ExAdd import *     //print exadd(1,10)     2.2 解说   这是一个最小的例子,只要一个文件ExAdd.dpr ,不需要任何其他的Pas Unit文件就可以了。   当我们把他放到py的syspath内,比如libsite-packages,在pywin内,可以做如下测试:     >>> from ExAdd import *     >>> print exadd(1,10)     11     >>>   可以看到,Python内的程序确实成功的调用了通过Delphi写的扩展。如何做到的? 2.3 如何注册一个模块   当Python内执行from ExAdd import *时,将会到syspath内寻找ExAdd.pyd,这里的pyd就是一般的dll,只不过还有一些约定。   当Py找到这个文件后,就调用引出函数initExAdd,这个函数的命名就是python程序和.pyd模块的的一个约定----函数命名必须为init+module名称。   一般来说,在init函数内,就进行引擎的初始化,模块的注册,函数,类型的注册等等工作。这里例子内,我们使用了TPythonEngine,   TPythonModule两个P4D提供的类,帮助我们做这些工作。   注册模块时,要注意     FModule.ModuleName := 'ExAdd';   内的ModuleName就是在Python内使用的模块完全一致,当然我们可以使用其他的名字,比如ExQuickAdd,只要from ExAdd import *内使用的   模块名称一致即可。为了方便和一致,我们可以约定dll的名字,python内的module,delphi内的TPythonModule名字完全一致。   这在语法上并非必须,不过这样做是一个很好的习惯。 2.4 如何注册一个函数   任何一个按照如下原型注册的函数,都可以被注册为PyExtention的函数。       function Add( Self, Args : PPyObject ) : PPyObject; far; cdecl;   其中cdecl说明服从C语言的调用规范,而不是Pascal或者其他。毕竟Python是C语言写就的,当然按照C语言的习惯来。   这个函数原型中,参数将会包括Self,Args,返回值得也是一个PPyObject,熟悉Python语言的都知道,任何一个Python函数在被调用时都会传递一个Self   指针进来,并且以Tuple的方式传递参数列表,这个Add函数的实现约定上也就表现出来了,所有的类型都是对象。比如Add(3,4)这个的Python调用,参照Add在Delphi中函数原型,   上,那么"3,4"作为一个Tuple对象,伴随Self,也是一个PPyObject,返回值7也是一个PPyObject来表达。   要不怎么都说Python慢呢?本来一个加操作可以直接对应汇编中的一个指令,现在又是对象又是指针,当然很难快了。   一旦有了这样的声明,就可以这样注册函数。      FModule.AddMethod( 'exadd', @Add, 'add(a,b) -> a+b ' );   以上语句向Python系统声明,exadd函数的实现在add内,最后参数作为__docstring__。当IDE内使用这个函数时,可以通过codeinsight,或者help来获得函数的使用说明。   现在来看add的实现代码。   一眼看过去,PyArg_ParseTuple,PyInt_FromLong是两个特别的东西。   PyArg_ParseTuple负责把传进来的args变成简单的Delphi类型,在Ppyobject内存储的3,4,分别存放到a,b:Integer内,就是       PyArg_ParseTuple( args, 'ii:Add', [@a, @b] )  0   其中第二个参数 'ii:Add' ,有些像是Format格式,i指明类型为Integer,两个I指明有两个整数,:add是可选的,当出错的时候,有:add,可以帮助程序员更好的找到错误。   这样就把PPyobeject所表达的PythonType转为一般Delphi类型;   而PyInt_FromLong这是想法,他把Delphi的Long类型转换为PyObject的Integer;从而可以让结果可以为Python识别。   这两个函数尽管是P4d实现的,但是和Python/C interface手册内规定的函数名称一致,因此具体的调用方法也可以看Python/C interface手册。   实际上Python实现内的对象表达采用了一个结构(Struct),很有一些复杂,我们现在可以在很高层的去看,要感谢P4D所做的工作。 3. 实现一个类   第一个例子可以工作,并且能够演示注册模块,函数和一些基本的Python Ext的概念。   对于长期使用Delphi这样的OO语言,仅仅公开函数当然不够方便,我们需要的是全OO编程,即使跨越了语言,也不会放弃这样的习惯。   我们现在要让Delphi的类可以为Python。 3.1 又一个例子 你首先看到的依然是一个例子,我们要把Delphi中的TPoint公开出来,让python可以调用,模块名称为dpoint,最终我们要在pythonIDE内看到的效果:     >>> from dpoint import *     >>> print SmallPoint(222,111)         >>> SmallPoint.__doc__     'wrapper for Delphi TPoint typen' P4D为注册类这样的工作提供了TPyDelphiWrapper类,在这个例子里,我们围绕这TPyDelphiWrapper来分析。 3.2 例子代码     library dpoint;     uses       Sharemem ,SysUtils,Classes,WrapDelphi,Types,PythonEngine;     {$E pyd}     var      FModule : TPythonModule;      FEngine:TPythonEngine ;      FDelphiWrapper : TPyDelphiWrapper;     procedure initdpoint; cdecl;     begin       FEngine := TPythonEngine.Create(nil);       FModule := TPythonModule.Create(nil);       FModule.Engine := FEngine;       FModule.ModuleName := 'dpoint';       FDelphiWrapper := TPyDelphiWrapper.Create(nil);       FDelphiWrapper.Engine := FEngine;       FDelphiWrapper.Module := FModule;       FEngine.LoadDll;     end;     exports       initdpoint;     var       OldExitProc: pointer;     procedure MyExitProc;     begin       FModule.Free;       FEngine.Free;       ExitProc := OldExitProc;     end;     type       TPyDelphiPoint = class(TPyObject)       private         fValue: TPoint;       protected       public         constructor CreateWith( APythonType : TPythonType; args : PPyObject ); override;         class procedure SetupType( PythonType : TPythonType ); override;       end;      Type       TTypesRegistration = class(TRegisteredUnit)       public         function Name : String; override;         procedure RegisterWrappers(APyDelphiWrapper : TPyDelphiWrapper); override;       end;           function TTypesRegistration.Name: String;     begin       Result := 'Types';     end;     procedure TTypesRegistration.RegisterWrappers(APyDelphiWrapper: TPyDelphiWrapper);     begin       inherited;       APyDelphiWrapper.RegisterHelperType(TPyDelphiPoint);     end;     constructor TPyDelphiPoint.CreateWith(APythonType: TPythonType;       args: PPyObject);     var       x, y : Integer;     begin       inherited;       if APythonType.Engine.PyArg_ParseTuple( args, 'ii:Create', [@x, @y] )  0 then       begin        fValue.X := x;        fValue.Y := y;       end     end;     class procedure TPyDelphiPoint.SetupType(PythonType: TPythonType);     begin       inherited;       PythonType.TypeName := 'SmallPoint';       PythonType.TypeFlags := PythonType.TypeFlags + [tpfBaseType];       PythonType.DocString.Text := '12345';     end;     begin       RegisteredUnits.Add(TTypesRegistration.Create);       OldExitProc := ExitProc;       ExitProc := @MyExitProc;     end. 3.3 注册过程 一个类必然要属于某一个模块,注册一个类就涉及到注册一个模块。关于注册模块,在例子中占据了不少带代码,但是它和第二部分完全一样,我们掠过不看。 本来注册一个类是有些复杂度的,如果想要知道这个复杂度,可以先看看参考文献1内的描述。不过采用P4D的类型注册框架就简单多了。 我们的例子pyd命名为dpoint ,我们准备把TPoint类型公开到Python内。 在initdpoint函数内,TPythonEngine,TPythonModule照样的初始化,比起函数注册来说,不同的地方在于创建了TPyDelphiWrapper的实例gDelphiWrapper, 并且指明他所属的PythonEngine,PythonModule。     procedure initdpoint;     begin         gEngine := TPythonEngine.Create(nil);         gEngine.AutoFinalize := False;         gModule := TPythonModule.Create(nil);         gModule.Engine := gEngine;         gModule.ModuleName := 'dpoint';         gDelphiWrapper := TPyDelphiWrapper.Create(nil);         gDelphiWrapper.Engine := gEngine;         gDelphiWrapper.Module := gModule;         gEngine.LoadDll;     end; gDelphiWrapper将会在RegisteredUnitList寻找RegisteredUnit,并且调用 这个类别内的RegisterWrappers方法,通过这个方法或者需要注册的Python类的Delphi包装类。 因此,我们要做的事情就是: 约定实现两个类,一个是需要公开的类型的Wrapper,这里就是TPyDelphiPoint,一个是注册这个Wrapper的注册类,本例子内就是TTypesRegistration。 TTypesRegistration只要实现两个覆盖基类的方法,从而达到通知TPyDelphiWrapper需要注册的类是TPyDelphiPoint。     function Name : String; override;     procedure RegisterWrappers(APyDelphiWrapper : TPyDelphiWrapper); override; 我们更多的注意力,尤其是以后的更多对PythonExtension特性的利用,集中于TPyDelphiPoint上。 TPyDelphiPoint,作为一个PythonType,最少要实现的方法有:     constructor CreateWith( APythonType : TPythonType; args : PPyObject ); override;         class procedure SetupType( PythonType : TPythonType ); override; 我们可以注意到,CreateWith传递的args依然是PPyObject类型,和前文谈到的add方法对参数和返回值的处理都是一致的。 SetupType将会指明在Python内如何使用这个类型,根据源代码知道,SetupType指明这个类型在Python内的类型为SmallPoint,提供基本服务(fvbase),类型文档__doc__为 '12345', 测试用例3.1代码如果正常运行,就自然的证实了这一点。 4.充分利用Python的特性 4.1 repr服务 以上例子很简单,但是可以表达主旨,是进一步了解和把握P4D编写扩展的基础。 从3.1的测试用例看,         >>> print SmallPoint(222,111)     这样的输出很不友好,我们希望他是这样的:         >>> print SmallPoint(222,111)     222,111 这样的服务在py内早已存在,它的名字叫做repr,每个对象如果希望打印友好,都应该支持这样的服务。 在Delphi编写的Py扩展中,如何做到这样的效果? 4.2 例子 一旦框架铺陈完毕,编写具体的功能就很简单了。repr服务只要覆盖一个方法,加上对返回参数的包装就可以了。 function  Repr : PPyObject; override; .. implementation .. function TPyDelphiPoint.Repr: PPyObject; begin   with GetPythonEngine do     Result := PyString_FromString(PChar(Format('', [Value.X, Value.Y]))); end; 4.3 更多 设置属性,需要覆盖RegisterGetSets方法: class procedure TPyDelphiPoint.RegisterGetSets(PythonType: TPythonType); begin   inherited;   with PythonType do     begin       AddGetSet('X', @TPyDelphiPoint.Get_X, @TPyDelphiPoint.Set_X,         'Provides access to the X coordinate of a point', nil);       AddGetSet('Y', @TPyDelphiPoint.Get_Y, @TPyDelphiPoint.Set_Y,         'Provides access to the Y coordinate of a point', nil);     end; end; 别忘了在SetupType内加入一行:    PythonType.Services.Basic := PythonType.Services.Basic+[bsGetAttrO, bsSetAttrO]; 告诉Python你的服务中有属性的支持。 允许dpoint之间比较大小,需要覆盖Compare方法: function TPyDelphiPoint.Compare(obj: PPyObject): Integer; var   _other : TPyDelphiPoint; begin   if IsDelphiObject(obj) and (PythonToDelphi(obj) is TPyDelphiPoint) then   begin     _other := TPyDelphiPoint(PythonToDelphi(obj));     Result := CompareValue(Value.X, _other.Value.X);     if Result = 0 then       Result := CompareValue(Value.Y, _other.Value.Y);   end   else     Result := 1; end; 同样别忘了在SetupType内加入一行:    PythonType.Services.Basic := PythonType.Services.Basic+[bsCompare]; 告诉Python你的服务中有bsCompare的支持。 5. 编写扩展后做什么? 5.1 P4d的代码值得已读,因为   基于注册的架构,dll直接到类TDynamicDll值得看,了解Python的内部实现,P4d本身就是Python和Delphi结合编程的良好榜样。 5.2 使用P4D还可以怎么样?   这个图景在我自己还并不很清楚。但是Python和Delphi的结合的愿望在我却一直很强烈,希望有更多的朋友参与进来,提出更多的想法。 参考: [1] Writing Python Extensions(Michael Hudsonhttp://starship.python.net/crew/mwh/toext/toext.html) [2] P4D Python For Delphi的缩写     从它的Change.txt内可以看到,这个P4D从1998年计算有接近10年的历史了。世道沧桑啊。 [3] Using Delphi and Python together     http://www.atug.com/andypatterns/pythonDelphiTalk.htm     很好的教程,可惜对输出类型这里语焉不详,不知道为什么。这也是我要写本文的动因。                                  

    发表于 @ 2008年06月24日 15:42:23|评论(loading...)|收藏

    新一篇: windows 最大物理内存 | 旧一篇: python Ini文件读写

    评论:没有评论。

    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © 蒋涛