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
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