使用C語言來擴充Python程式和Zope伺服器的教程

來源:互聯網
上載者:User
有幾個原因使您可能想用 C 擴充 Zope。最可能的是您有一個已能幫您做些事的現成的 C 庫,但是您對把它轉換成 Python 卻不感興趣。此外,由於 Python 是解釋性語言,所以任何被大量調用的 Python 代碼都將降低您的速度。因此,即使您已經用 Python 寫了一些擴充,您仍然要考慮把其中最常被調用的部分改用 C 來寫。不論哪種方式,擴充 Zope 都是從擴充 Python 開始。此外,擴充 Python 會給您帶來其它的好處,因為您的代碼將可以從任何 Python 指令碼訪問,而不只是從 Zope。這裡唯一要提醒的是在寫本文的時候,Python 的目前的版本是 2.1,但是 Zope 仍然只能和 Python 1.5.2 一起運行。對 C 擴充來說,兩個版本並沒有什麼變化,但如果您有興趣對您的庫進行 Python 封裝,又想讓它們都能在 Zope 下工作,您就得注意不要使用任何比 1.5.2 更新的東西。
Zope 是什嗎?

Zope 代表“Z Object Publishing Environment(Z 對象發布環境)”,它是用 Python 實現的應用程式伺服器。“太棒了,”您說,“但應用程式伺服器的確切含義是什麼呢?”應用程式伺服器就是一個長期啟動並執行進程,它為“活動的內容”提供服務。Web 服務器在運行期間調用應用程式伺服器來構建頁面。
擴充 Python:有趣又有益

想擴充 Zope,您首先要擴充 Python。雖然擴充 Python 不像“腦外科手術”那樣複雜,但也不像“在公園中散步”那樣悠閑。有兩個基本組件用於 Python 擴充。第一個顯然是 C 代碼。我將馬上探討它。 另一個部分是 安裝檔案。安裝檔案通過提供模組名稱、模組的 C 代碼的位置和您可能需要的所有編譯器標誌來描述模組。該檔案被預先處理,以建立 makefile(在 UNIX 上)或 MSVC++ 工程檔案(MSVC++ project file,在 Windows 上)。先說一下 ― Windows 上的 Python 事實上是用 Microsoft 編譯器編譯的。Python.org 的人也推薦用 MSVC++ 編譯擴充。顯然,您應該能夠成功說服 GNU 的編譯者們,但我本人還沒試過。

無論如何,還是讓我們來定義一個叫做‘foo'的模組吧。‘foo'模組會有一個叫做‘bar'的函數。當我們要使用時,我們可以用 import foo; 來把這個函數匯入到 Python 指令碼中,就跟匯入任何模組一樣。安裝檔案非常簡單:
清單 1. 一個典型的安裝檔案

# You can include comment lines. The *shared* directive indicates# that the following module(s) are to be compiled and linked for# dynamic loading as opposed to static: .so on Unix, .dll on Windows.*shared*# Then you can use the variables later using the $(variable) syntax# that 'make' uses. This next line defines our module and tells# Python where its source code is.foo foomain.c

編寫代碼

那麼我們實際上該怎樣寫 Python 知道如何使用的代碼呢,您問? foomain.c (當然,您可以隨意命名它)檔案包含三項內容:一個方法表,一個初始化函數和其餘的代碼。方法表簡單地將函數名與函數聯絡起來,並告知 Python 各個函數所使用的參數傳遞機制(您可以選擇使用一般的位置參數列表或位置參數和關鍵詞參數的混合列表)。Python 在模組裝入時調用初始化函數。初始化函數將完成模組所要求的所有初始化操作,但更重要的是,它還把一個指向方法表的指標傳回給 Python。

那我們就來看看我們的小型 foo 模組的 C 代碼。
清單 2. 一個典型的 Python 擴充模組

#include /* Define the method table. */static PyObject *foo_bar(PyObject *self, PyObject *args);static PyMethodDef FooMethods[] = {  {"bar", foo_bar, METH_VARARGS},  {NULL, NULL}};/* Here's the initialization function. We don't need to do anything  for our own needs, but Python needs that method table. */void initfoo(){  (void) Py_InitModule("foo", FooMethods);}/* Finally, let's do something ... involved ... as an example function. */static PyObject *foo_bar(PyObject *self, PyObject *args){  char *string;  int  len;  if (!PyArg_ParseTuple(args, "s", &string))    return NULL;  len = strlen(string);  return Py_BuildValue("i", len);}

深入研究

我們來看會兒這些代碼。首先,請注意您必須包含 Python.h 。除非您已在包含路徑(include path)中設定了該檔案的路徑,否則您可能需要在安裝檔案中包含 -I 標誌以指向該檔案。

初始化函數必須命名為 init <模組名>,在我們的例子中是 initfoo 。初始化函數的名稱,毫無疑問,是 Python 在裝入模組時所知道的關於模組的全部資訊,這也是初始化函數的名稱如此死板的原因。順便說一下,初始化函數必須是檔案中唯一未被聲明為 static 的全域識別碼。這對靜態連結比對動態連結更重要,因為非 static 標識符將是全域可見的。對動態連結來說,這不是一個很大的問題,但如果您打算在編譯期間連結所有東西,又沒有把所有可以聲明為 static 的東西聲明為 static ,那麼您很可能就會碰到名稱衝突的問題。

現在我們來觀察實際的代碼,看看參數是怎樣被處理的,傳回值又是怎樣被傳遞的。當然,一切都是 PyObject ― Python 堆上的對象。您從參數中得到的是一個對“this”對象的引用(this 用於對象方法,對類似 bar() 這樣的無參數的老式函數來說是 NULL)和一個儲存在 args 中的參數元組。您用 PyArg_ParseTuple 找回您的參數,然後用 Py_BuildValue 把結果傳回去。這些函數(還有更多)都歸檔在 Python 文檔的“Python/C API”部分中。不幸的是,沒有按名稱排列的簡單的函數清單,文檔是按主題排列的。

另請注意,函數在出錯的情況下返回 NULL。返回 NULL 表示出錯了;如果想讓 Python 做得更好,您應該拋出異常。我會指點您去查閱關於如何做這件事的文檔。

編譯擴充

現在剩下的全部問題是編譯模組。您可以通過兩種方式進行。第一種是按照文檔中的指導,運行 make -f Makefile.pre.in boot ,這樣將會使用您的 Setup 來編譯一個 Makefile。然後您就用該 Makefile 編譯您的工程。這種方式只適用於 UNIX。對 Windows 來說,存在一個叫“compile.py”的指令碼(請參閱本文後面的 參考資料)。原始指令碼很難找到;我從一個郵件清單中找到了一個來自 Robin Dunn(wxPython 的幕後工作者)的被大量改動了的副本。這個指令碼能在 UNIX 和 Windows 上工作;在 Windows 上,它將從您的 Setup 開始編譯 MSVC++ 工程檔案。

要進行編譯,您必須使包含的檔案和庫都可用。Python 的標準 Zope 安裝沒有包含這些檔案,因此您需要從 www.python.org(請參閱 參考資料)安裝 Python 的常規安裝。在 Windows 上,您還必須從原始碼安裝的 PC 目錄中擷取 config.h 檔案;它是 UNIX 安裝為您編譯的 config.h 的手工版。因此,在 UNIX 上,您應該已經擁有它了。

一旦這些都完成後,您就會得到一個以“.pyd”為副檔名的檔案。把這個檔案放到 Python 安裝目錄下的“lib”目錄(在 Zope 下,Python 位於“bin”目錄,因此您的擴充得結束於“bin/lib”目錄,奇怪吧。)然後您就可以調用它了,就像調用任何源生的 Python 模組一樣。

 >>> import foo; >>> foo.bar ("This is a test"); 14

做到這裡時,我的第一個問題是問自己該如何用 C 定義從 Python 中可見的 類。事實上,我可能問了一個錯誤的問題。在我已研究的樣本中,特定於 Python 的一切都只 用 Python 來完成,也都只調用從您的擴充中匯出的 C 函數。

把它帶到 Zope 中去

一旦完成了您的 Python 擴充,下一步就是使 Zope 能和它一起工作。您有幾種方式可以選擇,但在一定程度上,您希望您的擴充以什麼方式與 Zope 一起工作將首先影響到您編譯擴充的方式。從 Zope 內使用 Python(以及用 C 所做的擴充)代碼的基本方式是:

  • 如果函數很簡單,您可以把它當作一個變數。這些被叫做“外部方法”。
  • 更複雜的類,可以從 Zope 指令碼中調用(這是 Zope 2.3 的一個新功能)。
  • 您可以定義一個 Zope Product,然後可以用 ZClass(一組已做好的、Web 可訪問的對象)擴充它,在指令碼中使用它,根據它的自有許可權發布它(它的執行個體被當作頁來對待)。

當然,您自己的應用程式可以使用這些方式的組合。

建立外部方法

從 Zope 調用 Python 的最簡單的方式是把您的 Python 代碼做成 外部方法。外部方法是被放到 Zope 安裝目錄下的“Extensions”目錄中的 Python 函數。一旦那裡有了這樣一個 Python 檔案,您就可以轉到任意檔案夾,選擇“添加外部方法”,並添加調用要使用的函數的變數。然後您就可以往該檔案夾中顯示調用結果的任意頁添加 DTML 欄位。我們來看一個使用了上面所定義的 Python 擴充 ― foo.bar ― 的簡單樣本。

首先,來看擴充本身:我們把它放到一個例如叫 foo.pyd 的檔案中。記住,這個檔案位於 Zope 下的 Extensions 目錄。為了能夠順利進行,當然,我們在上面建立的 foo.pyd 必須在位於 bin/lib 的 Python 庫中。一個出於這個目的的、簡單的包看起來可能像這樣:
清單 3. 一個簡單的外部方法(檔案:Extensions/foo.py)

import foodef bar(self,arg):  """A simple external method."""  return 'Arg length: %d' % foo.bar(arg)

很簡單,不是嗎?它定義了一個可以用 Zope 管理介面附加到任意檔案夾的外部方法“bar”。要從該檔案夾中的任何頁中調用我們的擴充,我們只需簡單地插入一個 DTML 變數引用,如下所示:

 

當使用者查看我們的頁時,DTML 欄位將被文本“Arg length: 14”代替。我們就這樣用 C 擴充了 Zope。

Zope 指令碼:Cliff Notes 版

Zope 指令碼是 Python 2.3 的一個想用來代替外部方法的新功能。外部方法能做到的,它都能做到,而且它能和安全性及管理系統更好地整合,在整合方面提供更多的靈活性,它還有很多對 Zope API 中公開的全部 Zope 功能的訪問。

一個指令碼基本上就是一個短小的 Python 程式。它可以定義類或函數,但不是必須的。它被作為對象安裝在 Zope 檔案夾中,然後就可以把它當作 DTML 變數或調用(就像一個外部方法)來調用或者“從 Web 中”(在 Zope 中的意思就是它將被當作頁來調用)調用它。當然,這意味著指令碼可以像 CGI 程式那樣產生對錶單提交的響應,但卻沒有 CGI 的開銷。確實是一個很棒的功能。此外,指令碼有權訪問被調用者或調用者對象(通過“context”對象)、對象所在的檔案夾(通過“container”對象)和其他一些零碎資訊。要獲得更多關於指令碼的知識,請參閱 Zope 手冊(請參閱 參考資料)中的“進階 Zope 指令碼編製(Advanced Zope Scripting)”那一章。

您可能會錯誤地認為可以直接從指令碼簡單地匯入 foo 並使用 foo.bar(我知道我確實犯過這種錯誤)。但事實並非如此。由於安全性限制,只有 Product 可以被匯入,而不是什麼模組都可以。一般而言,Zope 的設計者們認為任何指令碼編製都需要訪問檔案系統,既然指令碼對象是由 Web 使用 Zope 管理介面來管理,所以它們不是完全可信的。所以我打算就此打住,不給您展示樣本指令碼了,而是來討論 Product 和基礎類。

專註於 Product

Product 是擴充 Zope 的強大工具方法。從安裝目錄的層級來看,Product 就是位於 Zope 目錄下的“lib/python/Products”目錄中的一個目錄。在您自己的 Zope 安裝目錄中,您可以看到很多 product 樣本,但本質上,最小的 Product 只由位於該目錄的兩個檔案組成:一個可任意命名的代碼檔案和一個 Zope 在啟動時調用來初始化 Product 的稱為 __init__.py 的檔案。(請注意:Zope 只在啟動時讀取 Product 檔案,這意味著為了測試,您必須能夠停止和重新啟動 Zope 進程)。本文只是盡量多提供一些您能通過使用 Zope Product 做到的事的提示。

要知道的是 Product 封裝了一個或多個可從 ZClass、指令碼或直接從 Web 上的 URL 使用的類。(當然,在最後一種情況下,Product 的執行個體被當作檔案夾看待;那麼 URL 的最後部分指定了將被調用的方法,該方法返回任意的 HTML。)您不必一定要把 Product 當作“可添加的”對象來對待,雖然這是它的主要目的。要看一個優秀的、現實存在的樣本,可以去看 ZCatalog 實現,它是標準 Zope 分發的一部分。那裡您可以在 __init__.py 中看到一個非常簡單的安裝指令碼,可以在 ZCatalog.py 中看到 ZCatalog 類,該類提供了很多發布方法。請注意 Zope 採用一種奇怪的約定來確定哪些方法可以通過 Web 存取 ― 如果一個方法包含有一個 doc 字串,那麼該方法可通過 Web 存取;否則,就被認為是私人的。

無論如何,我們還是來看一個使用了 C 模組(我們在上面定義了它)的非常簡單的 Product。首先來看非常簡單的 __init__.py;請注意它只做了一件事,即告訴 Zope 我們正在安裝的類的名稱。更複雜的初始化指令碼能做 更多的事,包括聲明由伺服器維護的全域變數以及設定存取權限等等。欲瞭解更多詳細資料,請參閱線上文檔中的 Zope 開發人員指南,也請研究您的 Zope 安裝目錄中現成的 Product。您或許已經猜到了,我們的樣本 Product 被稱為“Foo”。這樣您就將在 lib/python/Products 目錄下建立一個 Foo 子目錄。
清單 4. 基本的 Product 初始化指令碼

import Foodef initialize(context):  context.registerClass(    Foo.Foo,     permission='Add Foo',    constructors=Foo.manage_addFoo    )

現在請注意這個初始化指令碼不僅匯入了那個類,使它可被 Zope 的其它組件訪問,而且還將該類註冊成具有“可添加性”。 context.registerClass 調用通過首先命名我們所匯入的類,然後指定可被用於添加執行個體的方法名稱(這個方法必須顯示一個管理頁面,且該方法將自動與 Zope 管理介面整合)的名稱來完成這項工作。酷。

我們來小結一下這個短小、簡單的 Product。它會把我們的 foo.bar 函數公開給指令碼和 ZClass,並且還有一個作為“可添加的”對象的小介面,這就是全部內容。
清單 5. 一個簡單的 Zope Product

import fooclass Foo(SimpleItem.Item): "A Foo Product" meta_type = 'foo' def bar(self, string):   return foo.bar(string) def __init__(self, id):   "Initialize an instance"   self.id = id def index_html(self):   "Basic view of object"   return 'My id is %s and its length is %d.' % (self.id, foo.bar(self.id)) def manage_addFoo(self, RESPONSE):   "Management handler to add an instance to a folder."   self._setObject('Foo_id', Foo('Foo_id'))   RESPONSE.redirect('index_html')

這隻是一個最簡單的 Product。不能絕對地說它是可能的 Product 中最小的一個,但已經很接近了。不過,它確實說明了 Product 的一些關鍵特徵。首先,請注意“index_html”方法:它被調用來顯示一個對象執行個體,這是通過構建 HTML 完成的。它實際上是一個頁面。 manage_addFoo 方法是 Zope 對象管理的介面;我們在上面的 __init__.py 中引用了它。“__init__”方法初始化對象;實際上它 必須做的全部工作就是記錄執行個體的唯一識別碼。

這個微型的 Product 不和 Zope 安全性進行互動操作。它不做很多管理工作。它沒有互動功能。所以您可以給它添加很多東西(甚至連很有用的功能它也沒有)。我希望這對您是一個很好的開始。

以後該做什麼

對 Zope Product 的簡單介紹已經告訴您如何把 C 語言函數從 C 代碼變為 Zope 中可用的。要學會怎麼寫 Product,您還得閱讀更多文檔(其中有很多仍在完善之中),坦率地說,還要研究已有的 Product,看看它們是怎麼做的。Zope 模型有很強大的功能和很大的靈活性,它們都很值得探究。

我目前正在做整合 C 和 Zope 的大工程:整合我的工作流程工具包(workflow toolkit)。在本文發表之前,我希望能看到它的雛形。它已被列在下面的參考資料中,去看看吧;到您閱讀本文時,應該已經能夠從中找到一個擴充樣本。祝我好運。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.