Necə Pony (ORM) onun tövsiyələr edir?

Pony ORM , jeneratörü SQL-ə çevirmək üçün gözəl bir hiylədir. Məsələn:

 >>> 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]] >>> 

Pythonun böyük bir introspeksiyaya və metaproqramlaşdırmağa sahib olduğunu bilirəm, amma bu kitabxana əvvəlcədən işləmədən bir generator ifadəini necə tərcümə edə bilər? Sehr kimi görünür.

[yeniləmə]

Blender yazırdı:

Burada lazım olan fayl . Jeneratörün bir növ introspection manevrası ilə yenidən qurulması görünür. 100% Python sözdizimini dəstəklədiyindən əmin deyiləm, amma bu olduqca sərindir. - Blender

Mən düşünürdüm ki, onlar jeneratör protokolundan bəzi funksiyaları öyrənirdilər, lakin onlar bu ast baxırdılar və ast modulunun iştirak etdiyini ast ... Xeyr, proqramın qaynağını tezliklə yox etmirlərmi? Breathtaking ...

@BrenBarn: select funksiya çağırışı xaricində bir generator çağırmağa çalışıramsa, nəticəsidir:

 >>> 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 >>> 

Onlar Python sözdiziminin yaz qrammatik ağacını işlədilməsi və select funksiya çağırışının yoxlanılması kimi daha mürəkkəb spelllər yaratmaq kimi görünürlər.

Mən bunu hələ izah etmək istərdim, mənbə mənim səviyyəmdən kənara çıxır.

98
20 апр. Paulo Scardine tərəfindən 20 apr. 2013-04-20 04:53 '13 da 4:53 2013-04-20 04:53
ответ 1 cavab

Müəllif Pony ORM burada.

Pony Python generatorunu üç sətrdə bir SQL sorgusuna çevirir:

  • Bytecode generator və AST bərpa generatorundan (soyut sintaksis ağacı)
  • Python AST-in tərcüməsi "abstrakt SQL" - bir siyahıya əsaslanan bir SQL sorğunun ümumi təsviridir
  • SQL abstrakt nümayəndəliyini konkret verilənlər bazasına asılı SQL dialektinə çevirmək

Ən çətin hiss Pony Python ifadələrinin "mənasını" anlaması lazım olduğu ikinci addımdır. Görünür, ilk addımda ən çox maraqlanırsınız, buna görə dekompilyasiya necə işlədiyini izah edək.

Bu sorguyu nəzərdən keçirin:

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

Aşağıdakı SQL-ə nə tərcümə ediləcək:

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

Aşağıda çap olunacaq bu sorğunun nəticəsidir:

 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() funksiyası bir argument olaraq bir python generatoru alır və sonra onun bytecode analiz edir. Bu generatorun standart python dis modulundan istifadə edərək, bytecode təlimatlarını əldə edə bilərik:

 >>> 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, pony.orm.decompiling bytecode'dan geri ala biləcək pony.orm.decompiling modulunun içindəki decompile() funksiyasına malikdir:

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

Burada AST düyünlərinin mətn təqdimatını görə bilərik:

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

İndi decompile() funksiyasının necə işlədiyini görək.

Bu decompile() funksiyası qonaq modelini tətbiq edən Decompiler obyekti yaradır. Decompiler nümunəsi bytecode əmrlərini tək-tək qəbul edir. Hər komanda üçün decompiler obyekti öz metodunu çağırır. Bu metodun adı mövcud bytecode təlimatının adına bərabərdir.

Python bir ifadəni qiymətləndirərkən, hesablamanın ara nəticəsini saxlayan yığını istifadə edir. Decompiler obyekti də öz yığını var, lakin bu yığın ifadənin qiymətləndirilməsinin nəticəsini deyil, ifadə üçün AST nodunu ehtiva edir.

Decompiler üsulu növbəti byte kod təlimatı üçün çağırıldığında AST qovşaqlarını yığından alır, onları yeni AST nodeına birləşdirir və sonra bu nodu yığının üstünə qoyur.

Məsələn, c.country == 'USA' necə hesablandığını c.country == 'USA' . müvafiq bayt kod parçası:

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

Beləliklə, decompiler obyekti aşağıdakıları edir:

  • Çağırıb decompiler.LOAD_FAST('c') . Bu üsul, Decompiler yığının üst hissəsindəki Name('c') düyünü yerləşdirir.
  • Çağırıb decompiler.LOAD_ATTR('country') . Bu metod yığından bir Name('c') node alır, bir Geattr(Name('c'), 'country') node təşkil edir və yığını üstünə qoyur.
  • Çağırıb decompiler.LOAD_CONST('USA') . Bu metod yığın Const('USA') bir Const('USA') düyməsini yerləşdirir.
  • Çağırış decompiler.COMPARE_OP('==') . Bu metot, yığından iki Getidr və Const götürür və sonra yerləri Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))]) yığın hissələri.

Bytecode talimatlarını işlədildikdən sonra, decompiler yığın generatorun bütün ifadəsinə uyğun olan bir AST nodu ehtiva edir.

Pony ORM jeneratörləri və yalnız lambdanı ayırmaq lazımdır, çünki bu, çətin deyil, çünki generator üçün əmrlərin hərəkəti nisbətən sadədir - bu, yalnız bir neçə yuva looplarıdır.

Hal-hazırda, Pony ORM, iki şeyi istisna olmaqla, generatorun bütün əmrlərini əhatə edir:

  • Inline if ifadələr: a if b else c
  • Müqayisələr: a < b < c

Pony belə bir ifadə ilə qarşılaşırsa, bir NotImplementedError istisna atır. Ancaq hətta bu halda, jener ifadəsini simli olaraq keçərək onu işə sala bilərsiniz. Jeneratörü simli olaraq keçirdikdə, Pony decompiler modulunu istifadə etmir. Bunun yerinə, standart Python funksiyası compiler.parse istifadə edərək bir AST alır.

Ümid edirəm bu sualınıza cavab verir.

182
20 апр. Aleksandr Kozlovskinin aprelin 20-də verdiyi cavabı 2013-04-20 12:32 '13 at 12:32 2013-04-20 12:32

Tags ilə bağlı digər suallar və ya bir sual