কিভাবে পনি (ওআরএম) এর কৌশলগুলি করে?


111

কোনও জেনারেটর এক্সপ্রেশনকে এসকিউএল রূপান্তর করার সুন্দর কৌশলটি পনি ওআরএম করে। উদাহরণ:

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

আমি জানি পাইথনের দুর্দান্ত অন্তর্নির্মাণ এবং রূপকর্ম অন্তর্নির্মিত রয়েছে, তবে এই গ্রন্থাগারটি কীভাবে জেনারেটর এক্সপ্রেশনটিকে প্রিপ্রোসেসিং ছাড়াই অনুবাদ করতে সক্ষম হয়? দেখে মনে হচ্ছে যাদু।

[হালনাগাদ]

ব্লেন্ডার লিখেছেন:

আপনার পরে যে ফাইলটি রয়েছে তা এখানে । মনে হচ্ছে কিছু অন্তর্মুখী উইজার্ডারি ব্যবহার করে জেনারেটরটিকে পুনর্গঠন করা হবে। আমি নিশ্চিত না যে এটি পাইথনের সিনট্যাক্সের 100% সমর্থন করে কিনা তবে এটি দুর্দান্ত। - ব্লেন্ডার

আমি ভাবছিলাম তারা জেনারেটর এক্সপ্রেশন প্রোটোকল থেকে কিছু বৈশিষ্ট্য অন্বেষণ করছে, কিন্তু এই ফাইলটি সন্ধান করছে, এবং astমডিউলটিকে জড়িত দেখছে ... না, তারা ফ্লাইতে প্রোগ্রাম উত্সটি পরীক্ষা করছে না, তারা কি? অভূতপূর্ব...

@ ব্রেনবার্ন: যদি আমি জেনারেটরটিকে 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ফাংশন কলটি পরিদর্শন এবং ফ্লাইতে পাইথন অ্যাবস্ট্রাক্ট সিনট্যাক্স ব্যাকরণ গাছের প্রক্রিয়াজাতকরণের মতো আরও তীরচিহ্নগুলি করছে ।

আমি এখনও কাউকে এটি ব্যাখ্যা করতে দেখতে চাই, উত্সটি আমার জাদুকরী স্তরের বাইরে।


সম্ভবত pবস্তুটি পনি দ্বারা প্রয়োগ করা একটি ধরণের অবজেক্ট যা এতে কী কী পদ্ধতি / বৈশিষ্ট্যগুলি অ্যাক্সেস করা হচ্ছে তা দেখায় (যেমন name,, startswith) এবং সেগুলিকে এসকিউএলে রূপান্তর করে।
ব্রেণবার্ন

3
আপনার পরে যে ফাইলটি রয়েছে তা এখানে । মনে হচ্ছে কিছু অন্তর্মুখী উইজার্ডারি ব্যবহার করে জেনারেটরটিকে পুনর্গঠন করা হবে। আমি নিশ্চিত না যে এটি পাইথনের সিনট্যাক্সের 100% সমর্থন করে কিনা তবে এটি দুর্দান্ত।
ব্লেন্ডার

1
@ ব্লেন্ডার: আমি এলআইএসপি-তে এই জাতীয় কৌশল দেখেছি - পাইথনে এই স্টান্টটি টানতে পারা কেবল অসুস্থ!
পাওলো স্কার্ডাইন

উত্তর:


209

পনি ওআরএম লেখক এখানে আছেন।

পাইনি পাইথন জেনারেটরকে এসকিউএল কোয়েরিতে তিনটি পদক্ষেপে অনুবাদ করে:

  1. জেনারেটর বাইকোড এবং পুনর্নির্মাণ জেনারেটর এএসটি (বিমূর্ত সিনট্যাক্স ট্রি) এর সংযোগ
  2. পাইথন এএসটি অনুবাদ "বিমূর্ত এসকিউএল" - একটি এসকিউএল কোয়েরি সর্বজনীন তালিকা ভিত্তিক উপস্থাপনা
  3. বিমূর্ত এসকিউএল উপস্থাপনাকে নির্দিষ্ট ডাটাবেস-নির্ভর এসকিউএল উপভাষায় রূপান্তর করা

সবচেয়ে জটিল অংশটি দ্বিতীয় পদক্ষেপ, যেখানে পনিকে পাইথন এক্সপ্রেশনগুলির "অর্থ" বুঝতে হবে। দেখে মনে হচ্ছে আপনি প্রথম ধাপে সবচেয়ে আগ্রহী, সুতরাং কীভাবে পচনশীল কাজ করে তা আমাকে ব্যাখ্যা করতে দিন।

আসুন এই প্রশ্নটি বিবেচনা করুন:

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

যা নিম্নলিখিত এসকিউএল অনুবাদ করা হবে:

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()ফাংশন আর্গুমেন্ট হিসাবে একটি পাইথন জেনারেটরের গ্রহণ, এবং তারপর তার বাইটকোড বিশ্লেষণ। আমরা স্ট্যান্ডার্ড পাইথন 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

পনি ওআরএমের decompile()মডিউলটির মধ্যে ফাংশন রয়েছে pony.orm.decompilingযা বাইকোড থেকে একটি এএসটি পুনরুদ্ধার করতে পারে:

>>> 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বস্তু, যার ভিজিটর প্যাটার্ন প্রয়োগ করা হয়। ডিকম্পিলার উদাহরণটি বাই-কোড নির্দেশাবলী এক-এক করে পায়। প্রতিটি নির্দেশের জন্য ডেকম্পিলার বস্তু তার নিজস্ব পদ্ধতি কল করে। এই পদ্ধতির নাম বর্তমান বাইটকোড নির্দেশের নামের সমান।

পাইথন যখন একটি অভিব্যক্তি গণনা করে, তখন এটি স্ট্যাক ব্যবহার করে, যা গণনার মধ্যবর্তী ফলাফল সঞ্চয় করে। ডেকম্পিলার অবজেক্টটির নিজস্ব স্ট্যাকও রয়েছে তবে এই স্ট্যাকটি এক্সপ্রেশন গণনার ফলাফল নয়, তবে এএসটি নোডকে এক্সপ্রেশন হিসাবে রাখে।

পরবর্তী বাইটকোড নির্দেশের জন্য ডিকম্পিলার পদ্ধতিটি যখন ডাকা হয়, তখন এটি স্ট্যাক থেকে এএসটি নোড নেয়, তাদের একটি নতুন এএসটি নোডের সাথে সংযুক্ত করে এবং তারপরে এই নোডটিকে স্ট্যাকের শীর্ষে রাখে।

উদাহরণস্বরূপ, আসুন দেখে নেওয়া যাক কীভাবে সফলতা c.country == 'USA'গণনা করা হয়। সম্পর্কিত বাইকোড খণ্ডটি হ'ল:

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

সুতরাং, ডিকম্পিলার বস্তু নিম্নলিখিতটি করে:

  1. কল decompiler.LOAD_FAST('c')। এই পদ্ধতিটি 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'))]) স্ট্যাকের শীর্ষে রাখে ।

সমস্ত বাইকোড নির্দেশাবলী প্রক্রিয়া করার পরে, ডিকম্পিলার স্ট্যাকটিতে একটি একক এএসটি নোড থাকে যা পুরো জেনারেটরের এক্সপ্রেশনের সাথে মিলে যায়।

যেহেতু পনি ওআরএমকে কেবল জেনারেটর এবং ল্যাম্বডাসকেই নিষ্প্রভ করা দরকার, এটি এত জটিল নয়, কারণ জেনারেটরের জন্য নির্দেশের প্রবাহ তুলনামূলকভাবে সোজা - এটি কেবল নেস্টেড লুপগুলির একগুচ্ছ।

বর্তমানে পনি ওআরএম দুটি জিনিস বাদে পুরো জেনারেটরের নির্দেশাবলী সেট করে:

  1. এক্সপ্রেশন যদি ইনলাইন: a if b else c
  2. যৌগিক তুলনা: a < b < c

পনি যদি এই জাতীয় অভিব্যক্তির মুখোমুখি হয় তবে এটি NotImplementedErrorব্যতিক্রম উত্থাপন করে । তবে এই ক্ষেত্রেও আপনি জেনারেটর এক্সপ্রেশনটিকে স্ট্রিং হিসাবে পাস করে এটি কাজ করতে পারেন। আপনি যখন স্ট্রিং হিসাবে জেনারেটরটি পাস করেন পনি ডিকম্পিলার মডিউলটি ব্যবহার করবেন না। পরিবর্তে এটি স্ট্যান্ডার্ড পাইথন compiler.parseফাংশনটি ব্যবহার করে এটিএসটি পায় ।

আশা করি এটি আপনার প্রশ্নের উত্তর দেয়।


26
খুব পারফরম্যান্ট: (1) বাইটকোড ডেকমোপলিং খুব দ্রুত। (২) যেহেতু প্রতিটি ক্যোয়ারিতে সংশ্লিষ্ট কোড অবজেক্ট রয়েছে তাই এই কোড অবজেক্টটি ক্যাশে কী হিসাবে ব্যবহার করা যেতে পারে। এর কারণে, পনি ওআরএম প্রতিটি ক্যোয়ারি কেবল একবারই অনুবাদ করে, যেখানে জ্যাঙ্গো এবং এসকিউএএলএলচেমিকে একই কোয়েরিটি বারবার অনুবাদ করতে হয়। (3) পনি ওআরএম যেমন আইডেন্টিটি ম্যাপ প্যাটার্ন ব্যবহার করে, এটি একই লেনদেনের মধ্যে কোয়েরি ফলাফলগুলিকে ক্যাশে করে। একটি পোস্ট রয়েছে (রাশিয়ান ভাষায়) যেখানে লেখক বলেছেন যে পনি ওআরএম জ্যাঙ্গো এবং এসকিউএলএলচেমির চেয়ে 1.5.3 গুণ বেশি দ্রুতগতিতে বেরিয়েছে এমনকি কোয়েরি রেজাল্ট ক্যাচিং ছাড়াই: হ্যাব্রাহাব্ররু
আলেকজান্ডার

3
এটি কি পাইপি জেআইটি সংকলকের সাথে সামঞ্জস্যপূর্ণ?
Mzzl

2
আমি এটি পরীক্ষা করিনি, তবে কিছু রেডডিট কমেন্টার বলেছেন যে এটি সামঞ্জস্যপূর্ণ: tinyurl.com/ponyorm-pypy
আলেকজান্ডার কোজলভস্কি

9
এসকিউএলএলচেমির ক্যোয়ারী ক্যাচিং রয়েছে এবং ওআরএম এই বৈশিষ্ট্যটির ব্যাপক ব্যবহার করে। এটি ডিফল্টরূপে নয় কারণ এর সত্য সত্য আমাদের কোনও এসকিউএল এক্সপ্রেশন নির্মাণের সাথে সোর্স কোডের যে অবস্থানটি ঘোষণা করা হয়েছে তার সাথে সংযোগ স্থাপনের জন্য কোনও বৈশিষ্ট্য নেই যা কোড অবজেক্ট আপনাকে সত্যই যা দিচ্ছে তা। আমরা একই ফল পেতে স্ট্যাক ফ্রেম পরিদর্শন ব্যবহার করতে পারি তবে এটি আমার রুচিগুলির জন্য খুব সামান্য একটি হ্যাকি। এসকিউএল জেনারেশন যে কোনও ক্ষেত্রে সবচেয়ে কম সমালোচনামূলক পারফরম্যান্স অঞ্চল; সারি এবং বুককিপিং পরিবর্তনগুলি আনছে।
zzzeek

2
@ র্যান্ডমসুরফার_২৩ সম্ভবত না, এটি বাস্তবায়নের জন্য আমাদের কিছুটা সময় প্রয়োজন (সম্ভবত এক সপ্তাহ), এবং আমাদের কাছে আরও গুরুত্বপূর্ণ যে আরও কাজ রয়েছে।
আলেকজান্ডার কোজলভস্কি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.