2013-04-20 4 views
99

Pony ORM एसक्यूएल में जनरेटर अभिव्यक्ति को परिवर्तित करने की अच्छी चाल है। उदाहरण:कैसे टट्टू (ओआरएम) अपनी चाल करता है?

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

मैं जानता हूँ कि अजगर अद्भुत आत्मनिरीक्षण और metaprogramming निर्मित है, लेकिन कैसे इस पुस्तकालय preprocessing बिना जनरेटर अभिव्यक्ति का अनुवाद करने में सक्षम है? यह जादू की तरह दिखता है।

[अद्यतन]

ब्लेंडर ने लिखा है:

Here is the file है कि आप के बाद कर रहे हैं। ऐसा लगता है कि कुछ आत्मनिरीक्षण जादूगर का उपयोग करके जनरेटर का पुनर्निर्माण करना प्रतीत होता है। मुझे यकीन नहीं है कि यह 100% पायथन के वाक्यविन्यास का समर्थन करता है, लेकिन यह बहुत अच्छा है। - ब्लेंडर

मैं सोच रहा था कि वे जनरेटर अभिव्यक्ति प्रोटोकॉल से कुछ सुविधा की खोज कर रहे थे, लेकिन इस फाइल को देख, और 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 समारोह निरीक्षण की तरह अधिक रहस्यमय मंत्र कर रहे हैं फ्लाई पर पाइथन सार वाक्यविन्यास व्याकरण पेड़ को कॉल और प्रोसेसिंग करें।

मैं अभी भी इसे समझाते हुए देखना चाहता हूं, स्रोत मेरे जादूगर स्तर से परे है।

+0

संभावित रूप से 'पी' ऑब्जेक्ट टट्टू द्वारा लागू एक प्रकार का एक ऑब्जेक्ट है जो देखता है कि किस तरीके/गुणों पर इसका उपयोग किया जा रहा है (उदा।, 'नाम', 'startwith') और उन्हें SQL में परिवर्तित कर देता है। – BrenBarn

+2

[यहां] (https://github.com/ponyorm/pony/blob/orm/pony/orm/decompiling.py#L52) वह फ़ाइल है जिसके बाद आप हैं। ऐसा लगता है कि कुछ आत्मनिरीक्षण जादूगर का उपयोग करके जनरेटर का पुनर्निर्माण करना प्रतीत होता है। मुझे यकीन नहीं है कि यह 100% पायथन के वाक्यविन्यास का समर्थन करता है, लेकिन यह बहुत अच्छा है। – Blender

+1

@ ब्लेंडर: मैंने LISP में इस तरह की चाल देखी है - पाइथन में इस स्टंट को खींचना सिर्फ सादा बीमार है! –

उत्तर

183

टट्टू ओआरएम लेखक यहां है।

टट्टू SQL क्वेरी में अजगर जनरेटर तब्दील तीन चरणों में:

  1. जनरेटर बाईटकोड और पुनर्निर्माण जनरेटर एएसटी (सार वाक्य रचना पेड़) "सार एसक्यूएल" में अजगर AST के
  2. अनुवाद के Decompiling - सार्वभौमिक सूची के आधार पर एक SQL क्वेरी
  3. विशिष्ट डेटाबेस पर निर्भर एसक्यूएल बोली में परिवर्तित सार एसक्यूएल प्रतिनिधित्व के प्रतिनिधित्व

सबसे जटिल हिस्सा दूसरा चरण है, जहां टट्टू को पाइथन अभिव्यक्तियों के "अर्थ" को समझना चाहिए। ऐसा लगता है कि आप सबसे पहले पहले चरण में रूचि रखते हैं, तो मुझे बताएं कि कैसे कामकाजी काम करता है।

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

निम्नलिखित में से कौन एसक्यूएल में अनुवाद किया जाएगा:

id|email    |password|name   |country|address 
--+-------------------+--------+--------------+-------+--------- 
1 |[email protected] |***  |John Smith |USA |address 1 
2 |[email protected]|***  |Matthew Reed |USA |address 2 
4 |[email protected]|***  |Rebecca Lawson|USA |address 4 
:

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

और नीचे इस क्वेरी के परिणाम जो मुद्रित हो जाएगा

के इस क्वेरी पर विचार करें

select() फ़ंक्शन एक पायथन जनरेटर को तर्क के रूप में स्वीकार करता है, और टी मुर्गी इसके बाइटकोड का विश्लेषण करता है। हम मानक अजगर 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 

टट्टू ORM मॉड्यूल pony.orm.decompiling भीतर समारोह decompile() जो बाईटकोड से एक एएसटी बहाल कर सकते हैं है: यहाँ

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

, हम एएसटी नोड्स के पाठपरक प्रतिनिधित्व को देख सकते हैं:

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

चलो अब देखें कि decompile() फ़ंक्शन कैसे काम करता है।

decompile() फ़ंक्शन Decompiler ऑब्जेक्ट बनाता है, जो विज़िटर पैटर्न लागू करता है। डिकंपेलर इंस्टेंस बाइटकोड निर्देश एक-एक-एक प्राप्त करता है। प्रत्येक निर्देश के लिए decompiler ऑब्जेक्ट अपनी विधि कहता है। इस विधि का नाम वर्तमान बाइटकोड निर्देश के नाम के बराबर है।

जब पायथन एक अभिव्यक्ति की गणना करता है, तो यह स्टैक का उपयोग करता है, जो गणना के परिणामस्वरूप इंटरमीडिएट स्टोर करता है। डीकंपलर ऑब्जेक्ट का अपना स्टैक भी है, लेकिन यह स्टैक अभिव्यक्ति गणना के परिणाम, लेकिन अभिव्यक्ति के लिए एएसटी नोड का परिणाम नहीं है।

जब अगले बाईटकोड शिक्षा के लिए decompiler विधि कहा जाता है, यह ढेर से एएसटी नोड्स लेता है, एक नया एएसटी नोड में उन्हें को जोड़ती है, और फिर ढेर के शीर्ष पर इस नोड डालता है।

उदाहरण के लिए, देखते हैं कि subexpression c.country == 'USA' की गणना कैसे की जाती है। इसी बाईटकोड टुकड़ा है:

  1. कॉल decompiler.LOAD_FAST('c'):

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

    तो, decompiler वस्तु निम्नलिखित है। यह विधि decompiler स्टैक के शीर्ष पर Name('c') नोड रखती है।

  2. decompiler.LOAD_ATTR('country') पर कॉल करता है। यह विधि स्टैक से Name('c') नोड लेती है, Geattr(Name('c'), 'country') नोड बनाता है और इसे स्टैक के शीर्ष पर रखता है।
  3. decompiler.LOAD_CONST('USA') पर कॉल करता है। यह विधि स्टैक के शीर्ष पर Const('USA') नोड रखती है।
  4. कॉल decompiler.COMPARE_OP('==')। यह विधि स्टैक, से दो नोड्स (गेटैटर और कॉन्स) लेती है और फिर स्टैक के शीर्ष पर Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))]) डालती है।

आखिर बाईटकोड निर्देश कार्रवाई की जाती है, decompiler ढेर एक भी एएसटी नोड जो पूरे जनरेटर अभिव्यक्ति से मेल खाती है शामिल हैं।

के बाद से टट्टू ORM केवल जनरेटर और lambdas डिकंपाइल करने की जरूरत है, इस, कि जटिल नहीं है क्योंकि एक जनरेटर के लिए अनुदेश प्रवाह अपेक्षाकृत सीधा है - यह सिर्फ नेस्टेड छोरों का एक समूह है।

वर्तमान में टट्टू ORM पूरे जनरेटर निर्देश दो बातें छोड़कर सेट शामिल हैं:

  1. इनलाइन यदि भाव: a if b else c
  2. यौगिक तुलना: a < b < c

टट्टू ऐसी अभिव्यक्ति का सामना करना पड़ता अगर यह NotImplementedError को जन्म देती है अपवाद। लेकिन में भी इस मामले में आप जनरेटर अभिव्यक्ति को स्ट्रिंग के रूप में पास करके इसे काम कर सकते हैं। जब आप एक स्ट्रिंग के रूप में जनरेटर पास करते हैं तो टनी डिकंपेलर मॉड्यूल का उपयोग नहीं करता है। इसके बजाय इसे मानक पायथन compiler.parse फ़ंक्शन का उपयोग करके एएसटी प्राप्त करता है।

आशा है कि यह आपके प्रश्न का उत्तर देगा।

+1

महान स्पष्टीकरण, और पाइथन में लागू "आंतरिक डीएसएल" (डोमेन-विशिष्ट भाषा) का एक दिलचस्प उदाहरण है। अधिक के लिए http://stackoverflow.com/questions/tagged/dsl+python देखें - इस प्रश्न को [डीएसएल] के रूप में टैग करें। – RichVel

+0

बहुत बढ़िया! साझा करने के लिए धन्यवाद। – coleifer

+1

यह कितना सक्षम है? – ruipacheco

संबंधित मुद्दे