
小马ORM作者在这里。
Pony通过三个步骤将Python生成器转换为SQL查询:
- 反编译生成器字节码并重建生成器AST(抽象语法树)
- 将Python AST转换为“抽象SQL”-SQL查询的基于列表的通用表示形式
- 将抽象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 |john@example.com |*** |John Smith |USA |address 12 |matthew@example.com|*** |Matthew Reed |USA |address 24 |rebecca@example.com|*** |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_VALUEPony 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 (==)因此,反编译器对象执行以下 *** 作:
- 来电
decompiler.LOAD_FAST('c')。此方法将Name('c')节点放在反编译器堆栈的顶部。 - 来电
decompiler.LOAD_ATTR('country')。此方法Name('c')从堆栈中取出节点,创建该Geattr(Name('c'), 'country')节点并将其放在堆栈顶部。 - 来电
decompiler.LOAD_ConST('USA')。此方法将Const('USA')节点放在堆栈顶部。 - 来电
decompiler.COMPARE_OP('==')。此方法从堆栈中获取两个节点(Getattr和Const),然后将其Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])放在堆栈的顶部。
在处理完所有字节码指令之后,反编译器堆栈将包含一个与整个生成器表达式相对应的AST节点。
由于Pony ORM只需要反编译生成器和lambda,这并不那么复杂,因为生成器的指令流相对简单-它只是一堆嵌套循环。
目前,Pony ORM涵盖了整个生成器指令集,但以下两点除外:
- 内联if表达式:
a if b else c
- 复合比较:
a < b < c
如果Pony遇到此类表达,则会引发
NotImplementedError异常。但是即使在这种情况下,您也可以通过将生成器表达式作为字符串传递来使其工作。当您将生成器作为字符串传递时,Pony不使用反编译器模块。而是使用标准Python
compiler.parse函数获取AST
。
希望这能回答您的问题。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)