- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
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 查询:
最复杂的部分是第二步,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 (==)
因此,反编译器对象执行以下操作:
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 需要反编译生成器和 lambdas,这并不复杂,因为生成器的指令流程相对简单- 它只是一堆嵌套循环。
目前 Pony ORM 涵盖了整个生成器指令集,除了两件事:
a if b else c
a < b < c
如果 Pony 遇到这样的表达式,它会引发 NotImplementedError
异常(exception)。但即使在在这种情况下,您可以通过将生成器表达式作为字符串传递来使其工作。当您将生成器作为字符串传递时,Pony 不使用反编译器模块。反而它使用标准 Python compiler.parse
获取 AST功能。
希望这能回答你的问题。
关于python - Pony (ORM) 如何使用它的技巧?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16115713/
我有 2 个表。 TableA & TableB TableA +------+----------------+-----------+ | id | some_attribute | tabl
我正在尝试学习 Pony,出于显而易见的原因,我想做的第一件事就是打印值。 但是,它似乎不适用于大多数事情,例如: env.out.print(2 + 2) 给出错误: Could not infer
Pony 有一个未参数化的异常值。 不幸的是,我经常有一些代码想要抛出不同类型的异常,并且我需要知道它们是什么,以便正确处理它们——例如,简单地说,当停止程序时,向用户提供以下信息很重要正确的错误消息
我已经安装了pony.vim 我找不到任何文档来阅读如何使用它。我通过 vundle 安装它 gmarik vundle 如果我输入其中任何一个,我也不会得到任何帮助 :helptags pony :
执行 Pony ORM 查询并尝试按模型上存在的三个属性对查询进行排序。首先是歌曲类型,它可以是 ssf_type_order_map 中列出的五个值之一,然后是持续时间 (int) 和 uuid (
假设我用 pony.orm 映射了这些表: class Category(db.Entity): threads = Set("Thread") class Thread(db.Entity)
我想使用 pony orm 测试一张表是否为空。 起初我连接到数据库并说生成映射。我在这里使用“名称”表作为示例并连接到 postgres 数据库 from pony.orm import * cla
我正在尝试使用 Pony ORM 将多个值插入到我的 postgres 数据库中。我目前的方法效率很低: from pony.orm import * db = Database() class Na
很多时候,我写了如下查询: pony.orm.select(u for u in User if u.available and u.friends > 0 and ...) 所以,我想编写我自己的
Pony ORM将生成器表达式转换为 SQL 是一个不错的技巧。示例: >>> select(p for p in Person if p.name.startswith('Paul'))
我最近开始使用 Pony ORM,我认为它非常棒。尽管 API 在官方网站上有详细记录,但我在处理关系方面遇到了困难。特别是我想插入一个作为集合一部分的新实体。但是,我似乎无法找到一种在不先获取相关对
我尝试在 pony orm 中使用 order_by 聚合实现一个简单的选择: 所以,我尝试了以下方法:第一种方式引发错误信息: sel = select((f.Name, f.id) for f i
假设我在 Pony ORM 中有以下架构: from pony.orm import * db = Database("postgres", database='foo') class Job(db.
Pony ORM 的分页有什么最佳实践吗? 我看到其他人有这些 has_next 和 has_previous 辅助方法,但在 Pony 中我只能靠自己。 到目前为止,这就是我所拥有的,几个 Jinj
我正在评估从 peewee 到 Pony ORM 的转换。 peewee 中提供的一件好事是能够从这样的部分组成查询: def Entry(BaseModel): # peewee field
我无法让小马工作。现在我收到一个错误: TypeError: wrong argument (NilClass)! (Expected kind of OpenSSL::SSL:SSLContext)
我有一张报名表,要求填写人员姓名和电子邮件地址。我将该电子邮件地址保存到 session 中,以便在提交表单后访问它。然后我使用 Pony 向提交表单的人发送一封感谢/通知电子邮件。但是,虽然它可以毫
我想使用 Pony 邮件从我的 ruby 脚本发送一封电子邮件。当我为 gmail smtp 设置它时,它工作正常。当我将其设置为使用我们的 ISP 的 SMPT 时,出现此错误。 iiNet 代
我正在寻找的是具有计算机视觉经验的人关于哪种方法或算法最适合解决这个特定问题的建议。我是一位经验丰富的程序员(主要是 .NET),但我对计算机视觉几乎一无所知,我想节省时间。 我更喜欢不需要大型训练集
将带有附件(html 文件)的电子邮件发送到 gmail 时,不会发送电子邮件正文。如果我注释掉下面的附件设置就可以了。如果电子邮件没有任何附件,那就没问题。 我测试发现,只有当文件扩展名为“html
我是一名优秀的程序员,十分优秀!