跳转至内容
  • 版块
  • 标签
  • 热门
  • 用户
  • 群组
皮肤
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(Flatly)
  • 不使用皮肤
折叠

Odoo 中文社区

  1. 主页
  2. 版块
  3. Odoo 开发与实施交流
  4. OpenERP与Python 元编程

OpenERP与Python 元编程

已定时 已固定 已锁定 已移动 Odoo 开发与实施交流
12 帖子 7 发布者 21.5k 浏览
  • 从旧到新
  • 从新到旧
  • 最多赞同
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • digitalsatoriD 离线
    digitalsatoriD 离线
    digitalsatori 管理员
    写于 最后由 编辑
    #1

    Python元编程被称为“黑魔法”。Python界的传奇人物Tim Peters有云:
    [quote]Python的元编程这种黑魔法99%的人都无需了解,如果你拿不准是否应该用到它时,你不需要它.[/quote]
    OpenERP基本遵循了Tim Peters的教诲,但是却在6.1版本之后忍不住触及了一点点,从此游走于黑白两道之间:)

    其实OpenERP中用到的元类(MetaClass)作用非常简单:就是在V6.1后我们在模块中所定义的实体类,不需要进行实例化,比如
    OpenERPV6.1之前的实体类定义:

    <br />Class MyProduct(osv.osv):<br />&nbsp; &nbsp; _inherit=&quot;product.product&quot;<br />&nbsp; &nbsp; pass<br />MyProduct()<br />
    



    在OpenERPV6.1之后则不需要上面的类的实例化过程了。即,不需要下面这句了:

    <br />MyProduct()
    


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

    <br />class A(object):<br />&nbsp;  ...:&nbsp; &nbsp;  def __init__(self, x):<br />&nbsp;  ...:&nbsp; &nbsp; &nbsp; &nbsp;  self.x = x<br />&nbsp;  ...:&nbsp; &nbsp; &nbsp; &nbsp;  <br /><br />In [2]: a = A(2)<br /><br />In [3]: a<br />Out[3]: &lt;__main__.A at 0x2ff2fd0&gt;<br />
    


    a就是类A的一个实例对象。这是Python的基础知识,很好理解。但是,我要告诉你的是:在OpenERP中,MyProduct()并不产生MyProduct类的实例,甚至再深究的话,我们经常在代码中用到的pool.get('product.product')从对象池中获取的实例对象,也非这个MyProduct类所生成的实例。

    这到底是怎么一回事?我们首先要了解什么是类的实例化,或者类的调用到底是怎样的一个过程,比如上例中A(2),其实其执行过程基本上可以分为两个部分,用Python 来表示就是:

    <br />n = A.__new__(A, 2)&nbsp; &nbsp; &nbsp; #创建类的实例对象<br />if isinstance(n, A): A.__init__(n, 2)&nbsp; &nbsp;  #实例对象初始化<br />
    


    类A本身并没有定义__new__类方法,所以直接调用其父类即:object的__new__方法获得实例对象,如果获得的对象是A的实例则执行__init__方法
    同样的,Myproduct所继承的osv.osv类(或OE6.1以后称为BaseModel)就有一个__new__方法,而这个方法返回的是None,所以按照上面的说明它都不会运行到实例初始化的部分,当然也就无法获得Myproduct的实例。

    如果我们仔细分析代码,发现这个__new__的主要作用基本上就是下面这点代码:

    <br />&nbsp; &nbsp; &nbsp; &nbsp; module_model_list = MetaModel.module_to_models.setdefault(cls._module, &#91;])<br />&nbsp; &nbsp; &nbsp; &nbsp; if cls not in module_model_list:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if not cls._custom:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; module_model_list.append(cls)<br />
    


    其实MetaModel是一个元类(metaclass)等会儿要讲到,module_to_models则是这个类上的一个变量,其对应一个字典,字典的key对应每一个“模块”就是OpenERP的addons,其值对应这个模块中所定义的实体类(比如我们上例中的MyProduct)
    所以调用实体类并没有实例化,只是就这样登记备案了一下,事实上只有在模块载入(loading)过程中才会对所注册的实体类实例化,其实也不是一般意义的实例化,而是要另外创造一个新类,再做实例化。(这部分以后有空再介绍)

    那么问题回到原点,OpenERPV6.1以后如何做到,不调用实体类,即不运行BaseModel上的__new__方法就可以做到上述的类的注册过程。把OpenERP变色的那一点黑,这就出现了。对,就是那个叫MetaModel的家伙。在介绍MetaModel之前我们先快速的讲解一下Python的metaclass。
    在Python中一切皆为“对象”, 类的实例是对象,类本身也是对象。类的实例对象是通过对类的调用获得的,那么类本身这个对象又是如何获得的呢?
    其实上面的例子中类A的定义可以改写为:

    <br />A = type(&#039;A&#039;, (object,), {&#039;__init__&#039;: lambda self,x: self.x=x})<br />
    


    从上面的代码可以看出类A是通过调用type,或者是通过对type的实例化来获得的,事实上默认情况下所有的类都是由type实例化获得,这个type类就是所生成类的元类。
    类的实例对象可以对应五花八门我们定义的各种类,同理,我们是否可以定义除type以外用来生成类对象的五花八门的元类呢?答案当然是肯定的。看看我们的MetaModel:

    <br />class MetaModel(type):<br />&nbsp; &nbsp; ....<br />
    


    它与我们的普通的类定义没有什么差别,唯一需要注意的是其继承的父类是‘type',
    而在OpenERP所有的实体类的基类BaseModel的类定义中有这么一句:

    <br />__metaclass__ = MetaModel<br />
    


    这句有特殊的含义,表示该类对象将由元类MetaModel实例化生成。在Python3.x中则用如下的语法:

    <br />Class MyProduct(metaclass=MetaModel, osv.osv)<br />
    


    还记得上面提到的类的实例化,new, __init__两步,元类的实例化也是一样。
    我们看到MetaModel的__init__方法与上面提到的BaseModel类的__new__方法中有完全类似的代码:

    <br />&nbsp; &nbsp; &nbsp; &nbsp; if not self._custom:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.module_to_models.setdefault(self._module, &#91;]).append(self)<br />
    


    就是做了一个注册备案的动作。所以类对象本身产生的过程就已经注册了类,可以不用和6.0及以前版本的OpenERP每次定义实体类都要调用一下了。

    【上海先安科技】(tony AT openerp.cn)

    1 条回复 最后回复
    0
    • mrshellyM 离线
      mrshellyM 离线
      mrshelly
      写于 最后由 编辑
      #2

      学习.. 好深奥....

      校长. 我想如果使用 metaclass 是不是就是我一直想要的一个东西 的方向?
      我想根据一段代码(string) 生成一个 import 后的 module 对象?

      1 条回复 最后回复
      0
      • J 离线
        J 离线
        janden
        写于 最后由 编辑
        #3

        赞 深入技术贴

        1 条回复 最后回复
        0
        • digitalsatoriD 离线
          digitalsatoriD 离线
          digitalsatori 管理员
          写于 最后由 编辑
          #4

          [quote author=mrshelly link=topic=5771.msg14289#msg14289 date=1361832208]
          校长. 我想如果使用 metaclass 是不是就是我一直想要的一个东西 的方向?
          我想根据一段代码(string) 生成一个 import 后的 module 对象?
          [/quote]
          哈哈,看看Tim Peters 的回答!  “如果你拿不准是否应该用到它时,你不需要它“
          不过,好象真的用不到metaclass,下面的代码是不是你想要的? 但是,Python牛人们告诫我们,尽量不要用eval, exec等,那是既不安全又丑陋的,mrshelly就好这口没办法。

          code = &quot;&quot;&quot;<br />x = 100<br />class A(object):<br />&nbsp; &nbsp; def __init__(self, y):<br />&nbsp; &nbsp; &nbsp; &nbsp; print(&#039;x=%s&#039; % x)<br />&nbsp; &nbsp; &nbsp; &nbsp; print(&#039;y=%s&#039; % y)<br />&nbsp; &nbsp; &nbsp; &nbsp; &quot;&quot;&quot;<br />import imp<br />mymodule = imp.new_module(&#039;mymodule&#039;)<br />exec code in mymodule.__dict__<br /><br />In [41]: mymodule.A(2)<br />x=100<br />y=2<br />Out[41]: &lt;mymodule.A at 0x2e6c110&gt;<br />
          

          【上海先安科技】(tony AT openerp.cn)

          1 条回复 最后回复
          0
          • mrshellyM 离线
            mrshellyM 离线
            mrshelly
            写于 最后由 编辑
            #5

            哇, 这个必须顶 ...

            谢谢....

            1 条回复 最后回复
            0
            • mrshellyM 离线
              mrshellyM 离线
              mrshelly
              写于 最后由 编辑
              #6

              刚刚又 Google  到一种方法

              <br /><br />code = &#039;&#039;&#039;<br />x = 100<br />class A(object):<br />&nbsp; &nbsp; def __init__(self, y):<br />&nbsp; &nbsp; &nbsp; &nbsp; print(&#039;x=%s&#039; % x)<br />&nbsp; &nbsp; &nbsp; &nbsp; print(&#039;y=%s&#039; % y)<br />&#039;&#039;&#039;<br />import types<br />m = types.ModuleType(&#039;test_m&#039;)<br />exec code in m.__dict__<br /><br />import sys<br />sys.modules&#91;&#039;test_m&#039;] = m<br />
              



              然后  import test_m 就OK了.

              总结一下, 就是 使用 imp.new_module 或者  types.ModuleType 可以生成一个 module object 然后  再给 这个 object 添加 代码里的方法,属性

              添加的方法, 主要使用 exec 来玩

              再记录一下, 由 pyc 文件加载的代码

              <br /><br />import marshal<br />import types<br /><br />b = open(&#039;test_m.pyc&#039;, &#039;rb&#039;).read()<br />code = marshal.loads(b[8:])<br />m = types.ModuleType(&#039;test_m&#039;)<br />exec code in m.__dict__<br /><br />sys.modules&#91;&#039;test_m&#039;] = m<br /><br />#test<br />import test_m<br /><br />
              



              除了 exec 不爽点之外. 其他比较完美...
              code object 似乎也只能使用 exec 来导入到模块 object 中...

              1 条回复 最后回复
              0
              • W 离线
                W 离线
                wangbuke
                写于 最后由 编辑
                #7

                看了您的大作之后,欲罢不能,看了好几个小时代码,加上自己的理解。

                也得一文 OpenERP 模块动态加载原理及启动代码分析 http://buke.github.com/blog/2013/02/26/openerp-dynamic-loading-and-booting-way/

                请斧正 ~    ;D



                1 条回复 最后回复
                0
                • JoshuaJ 离线
                  JoshuaJ 离线
                  Joshua 管理员
                  写于 最后由 编辑
                  #8

                  😄 都是魔法师啊

                  【上海先安科技】(joshua AT openerp.cn),欢迎关注公众号:openerp_cn

                  1 条回复 最后回复
                  0
                  • digitalsatoriD 离线
                    digitalsatoriD 离线
                    digitalsatori 管理员
                    写于 最后由 编辑
                    #9

                    [quote author=wangbuke link=topic=5771.msg14298#msg14298 date=1361879273]
                    看了您的大作之后,欲罢不能,看了好几个小时代码,加上自己的理解。

                    也得一文 OpenERP 模块动态加载原理及启动代码分析 http://buke.github.com/blog/2013/02/26/openerp-dynamic-loading-and-booting-way/

                    请斧正 ~    ;D
                    [/quote]
                    把真正的强人引出来了,@wangbuke是OpenERP中文社区贡献高质量代码最多的人,文档协作组织者之一,他的强文一定要看。我在文章里卖的那个“以后再讨论”的关子,buke已清晰表达,还有更多学习热点。

                    【上海先安科技】(tony AT openerp.cn)

                    1 条回复 最后回复
                    0
                    • G 离线
                      G 离线
                      gotham
                      写于 最后由 编辑
                      #10

                      不错哇,比较深奥,搞python的教程都不教的东西。

                      1 条回复 最后回复
                      0
                      • M 离线
                        M 离线
                        mf1389004071
                        写于 最后由 编辑
                        #11

                        mark

                        1 条回复 最后回复
                        0

                        • 登录

                        • 没有帐号? 注册

                        • 登录或注册以进行搜索。
                        • 第一个帖子
                          最后一个帖子
                        0
                        • 版块
                        • 标签
                        • 热门
                        • 用户
                        • 群组