小马(ORM)如何发挥作用?

小马(ORM)如何发挥作用?,第1张

小马(ORM)如何发挥作用?

小马ORM作者在这里。

Pony通过三个步骤将Python生成器转换为SQL查询:

  1. 反编译生成器字节码并重建生成器AST(抽象语法树)
  2. 将Python AST转换为“抽象SQL”-SQL查询的基于列表的通用表示形式
  3. 将抽象SQL表示形式转换为特定于数据库的SQL方言

最复杂的部分是第二步,其中Pony必须理解Python表达式的“含义”。似乎您对第一步最感兴趣,所以让我解释一下反编译是如何工作的。

让我们考虑以下查询:

>>> from pony.orm.examples.estore import *>>> select(c for c in Customer if c.country == 'USA').show()

将其转换为以下SQL:

SELECT "c"."id", "c"."email", "c"."password", "c"."name", "c"."country", "c"."address"FROM "Customer" "c"WHERe "c"."country" = 'USA'

下面是该查询的结果,将其打印出来:

id|email   |password|name          |country|address  --+-------------------+--------+--------------+-------+---------1 |[email protected]   |***     |John Smith    |USA    |address 12 |[email protected]|***     |Matthew Reed  |USA    |address 24 |[email protected]|***     |Rebecca Lawson|USA    |address 4

select()
函数接受python生成器作为参数,然后分析其字节码。我们可以使用标准的python
dis
模块获取此生成器的字节码指令:

>>> gen = (c for c in Customer if c.country == 'USA')>>> import dis>>> dis.dis(gen.gi_frame.f_pre)  10 LOAD_FAST     0 (.0)        >>    3 FOR_ITER     26 (to 32)   6 STORE_FAST    1 (c)   9 LOAD_FAST     1 (c)  12 LOAD_ATTR     0 (country)  15 LOAD_ConST    0 ('USA')  18 COMPARE_OP    2 (==)  21 POP_JUMP_IF_FALSE        3  24 LOAD_FAST     1 (c)  27 YIELD_VALUE28 POP_TOP    29 JUMP_ABSOLUTE 3        >>   32 LOAD_ConST    1 (None)  35 RETURN_VALUE

Pony ORM

decompile()
在模块内
pony.orm.decompiling
具有可以从字节码恢复AST的功能:

>>> from pony.orm.decompiling import decompile>>> ast, external_names = decompile(gen)

在这里,我们可以看到AST节点的文本表示形式:

>>> astGenExpr(GenExprInner(Name('c'), [GenExprFor(AssName('c', 'OP_ASSIGN'), Name('.0'),[GenExprIf(Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))]))])]))

现在让我们看看该

decompile()
函数是如何工作的。

decompile()
函数创建一个
Decompiler
对象,该对象实现了Visitor模式。反编译器实例一一获取字节码指令。对于每条指令,反编译器对象都会调用其自己的方法。该方法的名称等于当前字节码指令的名称。

Python计算表达式时,将使用堆栈,该堆栈存储中间的计算结果。反编译器对象也有其自己的堆栈,但是此堆栈不存储表达式计算的结果,而是存储表达式的AST节点。

当调用下一个字节码指令的反编译器方法时,它将从堆栈中取出AST节点,将它们组合成一个新的AST节点,然后将该节点放在堆栈的顶部。

例如,让我们看看如何

c.country == 'USA'
计算子表达式。相应的字节码片段为:

   9 LOAD_FAST     1 (c)  12 LOAD_ATTR     0 (country)  15 LOAD_ConST    0 ('USA')  18 COMPARE_OP    2 (==)

因此,反编译器对象执行以下 *** 作:

  1. 来电
    decompiler.LOAD_FAST('c')
    。此方法将
    Name('c')
    节点放在反编译器堆栈的顶部。
  2. 来电
    decompiler.LOAD_ATTR('country')
    。此方法
    Name('c')
    从堆栈中取出节点,创建该
    Geattr(Name('c'), 'country')
    节点并将其放在堆栈顶部。
  3. 来电
    decompiler.LOAD_ConST('USA')
    。此方法将
    Const('USA')
    节点放在堆栈顶部。
  4. 来电
    decompiler.COMPARE_OP('==')
    。此方法从堆栈中获取两个节点(Getattr和Const),然后将其
    Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])
    放在堆栈的顶部。

在处理完所有字节码指令之后,反编译器堆栈将包含一个与整个生成器表达式相对应的AST节点。

由于Pony ORM只需要反编译生成器和lambda,这并不那么复杂,因为生成器的指令流相对简单-它只是一堆嵌套循环。

目前,Pony ORM涵盖了整个生成器指令集,但以下两点除外:

  1. 内联if表达式:
    a if b else c
  2. 复合比较:
    a < b < c

如果Pony遇到此类表达,则会引发

NotImplementedError
异常。但是即使在这种情况下,您也可以通过将生成器表达式作为字符串传递来使其工作。当您将生成器作为字符串传递时,Pony不使用反编译器模块。而是使用标准Python
compiler.parse
函数获取AST

希望这能回答您的问题。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/zaji/5662206.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存