日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

delphi打包python_使用Delphi 编写Python Extension

發布時間:2024/10/8 python 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 delphi打包python_使用Delphi 编写Python Extension 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

使用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的支持。

總結

以上是生活随笔為你收集整理的delphi打包python_使用Delphi 编写Python Extension的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。