OpenERP與Python 元編程

Python元編程被稱爲「黑魔法」。Python界的傳奇人物Tim Peters有云:git

引用編程

Python的元編程這種黑魔法99%的人都無需瞭解,若是你拿不許是否應該用到它時,你不須要它.

OpenERP基本遵循了Tim Peters的教誨,可是卻在6.1版本以後忍不住觸及了一點點,今後遊走於黑白兩道之間:)
其實OpenERP中用到的元類(MetaClass)做用很是簡單:就是在V6.1後咱們在模塊中所定義的實體類,不須要進行實例化,好比
OpenERPV6.1以前的實體類定義:app

程序代碼: [選擇].net

Class MyProduct(osv.osv):     _inherit="product.product"     pass
MyProduct()

在OpenERPV6.1以後則不須要上面的類的實例化過程了。即,不須要下面這句了:code

程序代碼: [選擇]對象

MyProduct()

爲了瞭解元類如何實現取消實例化過程,首先咱們來看一下OpenERP中實體類的實例化過程到底作了些什麼。
咱們通常知道,Python類的調用或稱爲實例化,會生成該類的一個實例對象(instance object),好比:繼承

程序代碼: [選擇]ci

class A(object):    ...:     def __init__(self, x):    ...:         self.x = x    ...:          In [2]: a = A(2)

In [3]: a
Technorati 標籤: Openerp, Python, 元編程
Out[3]: <__main__.A at 0x2ff2fd0>

a就是類A的一個實例對象。這是Python的基礎知識,很好理解。可是,我要告訴你的是:在OpenERP中,MyProduct()並不產生MyProduct類的實例,甚至再深究的話,咱們常常在代碼中用到的pool.get('product.product')從對象池中獲取的實例對象,也非這個MyProduct類所生成的實例。
這究竟是怎麼一回事?咱們首先要了解什麼是類的實例化,或者類的調用究竟是怎樣的一個過程,好比上例中A(2),其實其執行過程基本上能夠分爲兩個部分,用Python 來表示就是:get

程序代碼: [選擇]it

n = A.__new__(A, 2)      #建立類的實例對象
if isinstance(n, A): A.__init__(n, 2)     #實例對象初始化

類A自己並無定義__new__類方法,因此直接調用其父類即:object的__new__方法得到實例對象,若是得到的對象是A的實例則執行__init__方法
一樣的,Myproduct所繼承的osv.osv類(或OE6.1之後稱爲BaseModel)就有一個__new__方法,而這個方法返回的是None,因此按照上面的說明它都不會運行到實例初始化的部分,固然也就沒法得到Myproduct的實例。
若是咱們仔細分析代碼,發現這個__new__的主要做用基本上就是下面這點代碼:

程序代碼: [選擇]

        module_model_list = MetaModel.module_to_models.setdefault(cls._module, [])         if cls not in module_model_list:             if not cls._custom:                 module_model_list.append(cls)

其實MetaModel是一個元類(metaclass)等會兒要講到,module_to_models則是這個類上的一個變量,其對應一個字典,字典的key對應每個「模塊」就是OpenERP的addons,其值對應這個模塊中所定義的實體類(好比咱們上例中的MyProduct)
因此調用實體類並無實例化,只是就這樣登記備案了一下,事實上只有在模塊載入(loading)過程當中纔會對所註冊的實體類實例化,其實也不是通常意義的實例化,而是要另外創造一個新類,再作實例化。(這部分之後有空再介紹)
那麼問題回到原點,OpenERPV6.1之後如何作到,不調用實體類,即不運行BaseModel上的__new__方法就能夠作到上述的類的註冊過程。把OpenERP變色的那一點黑,這就出現了。對,就是那個叫MetaModel的傢伙。在介紹MetaModel以前咱們先快速的講解一下Python的metaclass。
在Python中一切皆爲「對象」, 類的實例是對象,類自己也是對象。類的實例對象是經過對類的調用得到的,那麼類自己這個對象又是如何得到的呢?
其實上面的例子中類A的定義能夠改寫爲:

程序代碼: [選擇]

A = type('A', (object,), {'__init__': lambda self,x: self.x=x})

從上面的代碼能夠看出類A是經過調用type,或者是經過對type的實例化來得到的,事實上默認狀況下全部的類都是由type實例化得到,這個type類就是所生成類的元類。
類的實例對象能夠對應五花八門咱們定義的各類類,同理,咱們是否能夠定義除type之外用來生成類對象的五花八門的元類呢?答案固然是確定的。看看咱們的MetaModel:

程序代碼: [選擇]

class MetaModel(type):     ....

它與咱們的普通的類定義沒有什麼差異,惟一須要注意的是其繼承的父類是‘type',
而在OpenERP全部的實體類的基類BaseModel的類定義中有這麼一句:

程序代碼: [選擇]

__metaclass__ = MetaModel

這句有特殊的含義,表示該類對象將由元類MetaModel實例化生成。在Python3.x中則用以下的語法:

程序代碼: [選擇]

Class MyProduct(metaclass=MetaModel, osv.osv)

還記得上面提到的類的實例化,__new__, __init__兩步,元類的實例化也是同樣。
咱們看到MetaModel的__init__方法與上面提到的BaseModel類的__new__方法中有徹底相似的代碼:

程序代碼: [選擇]

        if not self._custom:             self.module_to_models.setdefault(self._module, []).append(self)

就是作了一個註冊備案的動做。因此類對象自己產生的過程就已經註冊了類,能夠不用和6.0及之前版本的OpenERP每次定義實體類都要調用一下了。

« 最後編輯時間: 二月 25, 2013, 07:02:00 下午 做者 digitalsatori »

OpenERP高級實施顧問
上海先安科技 (http://cn.openerp.cn) tony AT openerp.cn 021 50323731

相關文章
相關標籤/搜索