gpt4 book ai didi

python - Pony (ORM) 如何使用它的技巧?

转载 作者:IT老高 更新时间:2023-10-28 21:06:49 27 4
gpt4 key购买 nike

Pony ORM将生成器表达式转换为 SQL 是一个不错的技巧。示例:

>>> select(p for p in Person if p.name.startswith('Paul'))
.order_by(Person.name)[:2]

SELECT "p"."id", "p"."name", "p"."age"
FROM "Person" "p"
WHERE "p"."name" LIKE "Paul%"
ORDER BY "p"."name"
LIMIT 2

[Person[3], Person[1]]
>>>

我知道 Python 具有出色的内省(introspection)和元编程内置功能,但是这个库如何能够在不进行预处理的情况下翻译生成器表达式?看起来很神奇。

[更新]

blender 写道:

Here is the file that you're after. It seems to reconstruct the generator using some introspection wizardry. I'm not sure if it supports 100% of Python's syntax, but this is pretty cool. – Blender

我以为他们正在探索生成器表达式协议(protocol)中的某些功能,但是查看此文件并看到涉及的 ast 模块...不,他们不是在动态检查程序源,是吗?令人兴奋...

@BrenBarn:如果我尝试在 select 函数调用之外调用生成器,结果是:

>>> x = (p for p in Person if p.age > 20)
>>> x.next()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "<interactive input>", line 1, in <genexpr>
File "C:\Python27\lib\site-packages\pony\orm\core.py", line 1822, in next
% self.entity.__name__)
File "C:\Python27\lib\site-packages\pony\utils.py", line 92, in throw
raise exc
TypeError: Use select(...) function or Person.select(...) method for iteration
>>>

似乎他们正在做更多神秘的咒语,例如检查 select 函数调用和动态处理 Python 抽象语法语法树。

我还是希望有人解释一下,来源远远超出了我的魔法水平。

最佳答案

Pony ORM 作者在这里。

Pony 分三步将 Python 生成器转换为 SQL 查询:

  1. 生成器字节码的反编译和生成器 AST 的重建(抽象语法树)
  2. 将 Python AST 翻译成“抽象 SQL”——通用SQL 查询的基于列表的表示
  3. 将抽象的 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 1
2 |matthew@example.com|*** |Matthew Reed |USA |address 2
4 |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_code)
1 0 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_VALUE
28 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 节点的文本表示:

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

现在让我们看看 decompile()功能有效。

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

当 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 需要反编译生成器和 lambdas,这并不复杂,因为生成器的指令流程相对简单- 它只是一堆嵌套循环。

目前 Pony ORM 涵盖了整个生成器指令集,除了两件事:

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

如果 Pony 遇到这样的表达式,它会引发 NotImplementedError异常(exception)。但即使在在这种情况下,您可以通过将生成器表达式作为字符串传递来使其工作。当您将生成器作为字符串传递时,Pony 不使用反编译器模块。反而它使用标准 Python compiler.parse 获取 AST功能。

希望这能回答你的问题。

关于python - Pony (ORM) 如何使用它的技巧?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16115713/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com