设为首页 收藏本站
查看: 925|回复: 0

[经验分享] OpenERP与Python 元编程

[复制链接]

尚未签到

发表于 2018-8-12 12:12:45 | 显示全部楼层 |阅读模式
  Python元编程被称为“黑魔法”。Python界的传奇人物Tim Peters有云:
  引用
Python的元编程这种黑魔法99%的人都无需了解,如果你拿不准是否应该用到它时,你不需要它.
  OpenERP基本遵循了Tim Peters的教诲,但是却在6.1版本之后忍不住触及了一点点,
Technorati 标签: Openerp,Python,元编程  从此游走于黑白两道之间:)
  其实OpenERP中用到的元类(MetaClass)作用非常简单:就是在V6.1后我们在模块中所定义的实体类,不需要进行实例化,比如
  OpenERPV6.1之前的实体类定义:
  程序代码: [选择]
Class MyProduct(osv.osv):     _inherit="product.product"     pass  
MyProduct()
  

  

  

  在OpenERPV6.1之后则不需要上面的类的实例化过程了。即,不需要下面这句了:
  程序代码: [选择]
  

  

MyProduct()  

  

  为了了解元类如何实现取消实例化过程,首先我们来看一下OpenERP中实体类的实例化过程到底做了些什么。
  我们一般知道,Python类的调用或称为实例化,会生成该类的一个实例对象(instance object),比如:
  程序代码: [选择]
  

  

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 来表示就是:
  程序代码: [选择]
  

  

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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-550608-1-1.html 上篇帖子: Python IDE介绍 下篇帖子: Python -- collection 系列
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表