ผู้เขียน Pony ORM อยู่ที่นี่
Pony แปล Python Generator เป็น SQL query ในสามขั้นตอน:
- การแยกตัวสร้าง bytecode ของเครื่องกำเนิดและการสร้าง AST (โครงสร้างไวยากรณ์แบบนามธรรม)
- การแปล Python AST เป็น "abstract 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 1
2 |matthew@example.com|*** |Matthew Reed |USA |address 2
4 |rebecca@example.com|*** |Rebecca Lawson|USA |address 4
select()
ฟังก์ชั่นยอมรับกำเนิดหลามเป็นอาร์กิวเมนต์แล้ววิเคราะห์ bytecode ของมัน เราสามารถรับคำแนะนำ bytecode ของเครื่องกำเนิดไฟฟ้านี้โดยใช้dis
โมดูลpython มาตรฐาน:
>>> 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 จาก bytecode:
>>> 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
วัตถุซึ่งดำเนินรูปแบบของผู้เข้าชม อินสแตนซ์ตัวถอดรหัสจะได้รับคำสั่ง bytecode ทีละรายการ สำหรับแต่ละคำสั่งอ็อบเจ็กต์ decompiler จะเรียกใช้เมธอดของตัวเอง ชื่อของเมธอดนี้เท่ากับชื่อของคำสั่ง bytecode ปัจจุบัน
เมื่อ Python คำนวณนิพจน์จะใช้ stack ซึ่งเก็บผลการคำนวณระดับกลาง อ็อบเจ็กต์ decompiler ยังมีสแต็กของตัวเอง แต่สแต็กนี้ไม่เก็บผลลัพธ์ของการคำนวณนิพจน์ แต่เป็นโหนด AST สำหรับนิพจน์
เมื่อมีการเรียกวิธีการถอดรหัสสำหรับคำสั่ง bytecode ถัดไปจะใช้โหนด AST จากสแต็กรวมเข้ากับโหนด AST ใหม่จากนั้นวางโหนดนี้ไว้ที่ด้านบนสุดของสแต็ก
ตัวอย่างเช่นเรามาดูวิธีc.country == 'USA'
คำนวณนิพจน์ย่อย ส่วน bytecode ที่เกี่ยวข้องคือ:
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'))])
ไว้ที่ด้านบนสุดของสแต็ก
หลังจากประมวลผลคำสั่ง bytecode ทั้งหมดแล้ว decompiler stack จะมีโหนด AST เดียวซึ่งสอดคล้องกับนิพจน์ตัวสร้างทั้งหมด
เนื่องจาก Pony ORM จำเป็นต้องแยกเครื่องกำเนิดไฟฟ้าและแลมบ์ดาสเท่านั้นสิ่งนี้จึงไม่ซับซ้อนนักเนื่องจากขั้นตอนการเรียนการสอนสำหรับเครื่องกำเนิดไฟฟ้านั้นค่อนข้างตรงไปตรงมามันเป็นเพียงการวนซ้ำที่ซ้อนกัน
ปัจจุบัน Pony ORM ครอบคลุมชุดคำสั่งของเครื่องกำเนิดไฟฟ้าทั้งหมดยกเว้นสองสิ่ง:
- อินไลน์ if นิพจน์:
a if b else c
- การเปรียบเทียบเชิงเปรียบเทียบ:
a < b < c
หาก Pony พบการแสดงออกดังกล่าวจะทำให้เกิดNotImplementedError
ข้อยกเว้น แต่ในกรณีนี้คุณสามารถทำให้มันทำงานได้โดยส่งนิพจน์ตัวสร้างเป็นสตริง เมื่อคุณส่งเครื่องกำเนิดไฟฟ้าเป็นสตริง Pony จะไม่ใช้โมดูลถอดรหัส แต่จะได้รับ AST โดยใช้ Python มาตรฐานcompiler.parse
ฟังก์ชัน
หวังว่านี่จะตอบคำถามของคุณ
p
วัตถุเป็นวัตถุของประเภทดำเนินการโดยม้าที่มีลักษณะที่สิ่งที่วิธีการ / คุณสมบัติที่มีการเข้าถึงได้ (เช่นการname
,startswith
) และแปลงให้กับ SQL