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