Pony ORM melakukan trik bagus untuk mengubah ekspresi generator menjadi SQL. Contoh:
>>> 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]]
>>>
Saya tahu Python memiliki introspeksi dan metaprogramming yang luar biasa, tetapi bagaimana pustaka ini dapat menerjemahkan ekspresi generator tanpa preprocessing? Sepertinya ajaib.
[memperbarui]
Blender menulis:
Ini file yang Anda cari. Tampaknya merekonstruksi generator menggunakan beberapa sihir introspeksi. Saya tidak yakin apakah itu mendukung 100% sintaks Python, tetapi ini cukup keren. - Blender
Saya berpikir mereka menjelajahi beberapa fitur dari protokol ekspresi generator, tetapi melihat file ini, dan melihat ast
modul yang terlibat ... Tidak, mereka tidak memeriksa sumber program dengan cepat, bukan? Menakjubkan...
@BrenBarn: Jika saya mencoba memanggil generator di luar select
pemanggilan fungsi, hasilnya adalah:
>>> 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
>>>
Sepertinya mereka melakukan lebih banyak mantra rahasia seperti memeriksa select
pemanggilan fungsi dan memproses pohon tata bahasa sintaks abstrak Python dengan cepat.
Saya masih ingin melihat seseorang menjelaskannya, sumbernya jauh melampaui tingkat sihir saya.
p
objek tersebut adalah objek dari tipe yang diimplementasikan oleh Pony yang melihat metode / properti apa yang sedang diakses padanya (misalnyaname
,startswith
) dan mengubahnya menjadi SQL.Jawaban:
Penulis Pony ORM ada di sini.
Pony menerjemahkan generator Python ke dalam kueri SQL dalam tiga langkah:
Bagian paling kompleks adalah langkah kedua, di mana Pony harus memahami "arti" dari ekspresi Python. Sepertinya Anda paling tertarik pada langkah pertama, jadi izinkan saya menjelaskan cara kerja dekompilasi.
Mari pertimbangkan kueri ini:
Yang akan diterjemahkan ke dalam SQL berikut:
Dan di bawah ini adalah hasil dari query ini yang akan dicetak:
The
select()
Fungsi menerima generator python sebagai argumen, dan kemudian menganalisa bytecode nya. Kita bisa mendapatkan instruksi bytecode dari generator ini menggunakandis
modul python standar :Pony ORM memiliki fungsi di
decompile()
dalam modulpony.orm.decompiling
yang dapat mengembalikan AST dari bytecode:Di sini, kita dapat melihat representasi tekstual dari node AST:
Sekarang mari kita lihat bagaimana
decompile()
fungsinya bekerja.The
decompile()
fungsi menciptakanDecompiler
objek, yang menerapkan pola pengunjung. Instance decompiler mendapatkan instruksi bytecode satu per satu. Untuk setiap instruksi, objek decompiler memanggil metodenya sendiri. Nama metode ini sama dengan nama instruksi bytecode saat ini.Saat menghitung ekspresi, Python menggunakan tumpukan, yang menyimpan hasil penghitungan antara. Objek decompiler juga memiliki tumpukannya sendiri, tetapi tumpukan ini tidak menyimpan hasil kalkulasi ekspresi, tetapi node AST untuk ekspresi tersebut.
Ketika metode decompiler untuk instruksi bytecode berikutnya dipanggil, ia mengambil node AST dari stack, menggabungkannya menjadi node AST baru, dan kemudian meletakkan node ini di atas stack.
Misalnya, mari kita lihat bagaimana subekspresi
c.country == 'USA'
dihitung. Fragmen bytecode terkait adalah:Jadi, objek decompiler melakukan hal berikut:
decompiler.LOAD_FAST('c')
. Metode ini menempatkanName('c')
node di atas tumpukan dekompiler.decompiler.LOAD_ATTR('country')
. Metode ini mengambilName('c')
node dari tumpukan, membuatGeattr(Name('c'), 'country')
node dan meletakkannya di atas tumpukan.decompiler.LOAD_CONST('USA')
. Metode ini menempatkanConst('USA')
node di atas tumpukan.decompiler.COMPARE_OP('==')
. Metode ini mengambil dua node (Getattr dan Const) dari tumpukan, dan kemudian meletakkannyaCompare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])
di atas tumpukan.Setelah semua instruksi bytecode diproses, tumpukan dekompiler berisi satu node AST yang sesuai dengan ekspresi generator secara keseluruhan.
Karena Pony ORM perlu mendekompilasi generator dan lambda saja, ini tidak terlalu rumit, karena aliran instruksi untuk generator relatif mudah - ini hanya sekumpulan loop bersarang.
Saat ini Pony ORM mencakup seluruh set instruksi generator kecuali dua hal:
a if b else c
a < b < c
Jika Pony menemukan ekspresi seperti itu, itu akan menimbulkan
NotImplementedError
pengecualian. Tetapi bahkan dalam kasus ini Anda dapat membuatnya bekerja dengan meneruskan ekspresi generator sebagai string. Ketika Anda melewatkan generator sebagai string, Pony tidak menggunakan modul decompiler. Sebagai gantinya, ia mendapatkan AST menggunakancompiler.parse
fungsi Python standar .Semoga ini menjawab pertanyaan Anda.
sumber