SORU
20 NİSAN 2013, CUMARTESİ


Nasıl Pony (ORM) yaptığı hileler?

Pony ORM SQL içine üreteci bir ifade dönüştürme güzel işe yarıyor. Örnek:

>>> 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 harika içgözlem ve metaprogramming yerleşik var, ama bu kütüphane önişleme olmadan jeneratörü ifade çevirmek mümkün olduğunu nasıl biliyorum? Sihirli gibi görünüyor.

[güncelleme]

Blender yazdı:

Sonra sen o Here is the file. Jeneratör bazı içgözlem büyücülük kullanarak yeniden inşa gibi görünüyor. Eğer Python sözdizimi 0 destekliyor mu emin değilim, ama bu gerçekten çok güzel. – Blender

Ben düşündüler keşfetmek bazı özellik jeneratörü ifade protokolü, ama bu dosya, ve görmek ast modül dahil... Hayır, değildir teftiş programı kaynak fly. Akıllara zarar...

Eğer jeneratör select işlev çağrısı dışında aramaya çalışıyorum, sonuç: @BrenBarn:

>>> 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 işlev çağrısı teftiş ve Python soyut sözdizimi dilbilgisi işleme gibi arcane büyüler yapıyorlarmış gibi anında ağaç gibi görünüyor.

Ben hala bir açıklama görmek istiyor, kaynak yolu büyücülük düzeyi ötesinde.

CEVAP
20 NİSAN 2013, CUMARTESİ


ORM yazar burada Midilli.

Midilli üç adımda SQL sorgu içine Python jeneratör bizimle iletişime geçiniz

  1. Jeneratör bayt kodu olan vermeyeceksiniz ve yeniden jeneratör AST (soyut sözdizimi ağacı)
  2. Python çeviri AST "soyut SQL" ... evrensel liste tabanlı bir SQL sorgu gösterimi
  3. Dönüştürme soyut SQL özel olarak temsil veritabanı bağımlı SQL lehçesi

En karmaşık bölümü Midilli gereken ikinci adım. ""Python ifadeler. anlam anlamak Çoğu gibi görünüyor ilk adım ilgileniyor, bu yüzden bana vermeyeceksiniz nasıl işliyor anlatayım.

Hadi bu sorgu göz önünde bulundurun:

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

Aşağıdaki SQL çevrilmiş olacak:

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

Ve aşağıda yazdırılır bu sorgu sonucudur:

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() işlevi argüman olarak bir python jeneratör kabul eder ve sonra kendi koduna analiz eder. Bu jeneratör bayt talimatları standart python dis modül: kullanarak elde edebiliriz

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

Midilli ORM vardır 21 ** modüldeki fonksiyonu decompile() olabilir bayt kodu bir AST geri:

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

Burada, AST düğümleri metinsel gösterimi görebilirsiniz:

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

Hadi decompile() işlevin nasıl çalıştığını şimdi anlıyorum.

decompile() işlevi Ziyaretçi desen uygulayan Decompiler bir nesne oluşturur. Kodlama örneği bayt talimatları tek tek alır. Her talimat için ekrandaki herhangi bir nesneyi kendi yöntemini çağırır. Bu yöntemin adı geçerli bir bayt kodu talimat adını eşittir.

Python bir ifade hesaplarken, bir ara saklayan yığını kullanır hesaplamanın sonucu. Kendi yığını ekrandaki herhangi bir nesne de, ama bu depolar ifade hesaplamanın sonucu değil, yığın, ama AST ifadesi için düğüm.

Sonraki bayt kodu talimat EXE yöntem çağrıldığında, yığından AST düğümleri alır, birleştirir onları yeni bir AST düğüm, ve sonra yığın. üstteki bu düğüm koyar içine

Örneğin, c.country == 'USA' nasıl hesaplandığını görelim. Bu karşılık gelen bayt kodu parçası

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

Bu yüzden, ekrandaki herhangi bir nesnenin şunları yapar:

  1. Aramalar decompiler.LOAD_FAST('c'). Bu yöntem programı, yığın üstündeki Name('c') düğüm atıyor.
  2. Aramalar decompiler.LOAD_ATTR('country'). Bu yöntem yığından, Name('c') düğüm alır Geattr(Name('c'), 'country') düğüm oluşturur ve yığın. üstüne koyar
  3. Aramalar decompiler.LOAD_CONST('USA'). Bu yöntem üst yığını Const('USA') düğüm atıyor.
  4. Aramalar decompiler.COMPARE_OP('=='). Bu yöntem, iki düğüm (Getattr ve Sabit) yığından alır ve sonra koyar Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))]) yığın. üstteki

Tüm bayt kodu talimat işlendikten sonra, ekrandaki yığını içerir tüm jeneratör ifadesine karşılık gelen tek AST bir düğüm.

Midilli ORM jeneratörler derleme ihtiyacı olduğundan ve Lambda sadece, bu karmaşık, çünkü değil bir jeneratör için talimat akışı nispeten basittir - iç içe döngüler sadece bir demet.

Şu anda Midilli ORM Kapakları Tüm jeneratör talimatları iki şey hariç:

  1. İfadeler eğer satır içi: a if b else c
  2. Bileşik karşılaştırmalar: a < b < c

Eğer Midilli gibi ifade karşılaşırsa NotImplementedError özel durum yükseltir. Bile ama bu durumda bu iş bir dize jeneratör ifade geçirerek yapabilirsiniz. Bir dize olarak bir jeneratör geçirdiğinizde Midilli kodlama modülü kullanmıyor. Yerine AST compiler.parse fonksiyon standart Python kullanarak alır.

Bu senin sorunu cevaplar umarım.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Charles Renaud

    Charles Rena

    10 Kasım 2007
  • Google Analytics

    Google Analy

    25 ŞUBAT 2008
  • Simon Hayter

    Simon Hayter

    20 HAZİRAN 2010