Python __setattr__ و __getattr__ للنطاق العالمي؟
-
27-09-2019 - |
سؤال
لنفترض أنني بحاجة إلى إنشاء DSL الصغيرة الخاصة بي التي من شأنها أن تستخدم Python لوصف بنية بيانات معينة. على سبيل المثال ، أود أن أتمكن من كتابة شيء مثل
f(x) = some_stuff(a,b,c)
والحصول على بيثون ، بدلاً من الشكوى من المعرفات غير المعلنة أو محاولة استدعاء الوظيفة some_stuff ، قم بتحويلها إلى تعبير حرفي لراحتي الإضافية.
من الممكن الحصول على تقريب معقول لهذا من خلال إنشاء فئة مع إعادة تعريفها بشكل صحيح __getattr__
و __setattr__
الأساليب واستخدامها على النحو التالي:
e = Expression()
e.f[e.x] = e.some_stuff(e.a, e.b, e.c)
سيكون من الرائع رغم ذلك ، إذا كان من الممكن التخلص من "هـ" المزعج. البادئات وربما حتى تجنب استخدام []. لذلك كنت أتساءل ، هل من الممكن أن يكون "إعادة تعريف" عمليات البحث والمهام العالمية بشكل مؤقت؟ في ملاحظة ذات صلة ، ربما هناك حزم جيدة لتحقيق هذه الوظائف "الاقتباس" بسهولة لتعبيرات بيثون؟
المحلول
لست متأكدًا من أنها فكرة جيدة ، لكنني اعتقدت أنني سأفعل ذلك جربها. كي تختصر:
class PermissiveDict(dict):
default = None
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
return self.default
def exec_with_default(code, default=None):
ns = PermissiveDict()
ns.default = default
exec code in ns
return ns
نصائح أخرى
قد ترغب في إلقاء نظرة على ast
أو parser
الوحدات النمطية المدرجة مع Python لتحليل ، وصول وتحويل شجرة بناء الجملة المجردة (أو شجرة التحليل ، على التوالي) من رمز الإدخال. بقدر ما أعرف ، النظام الرياضي حكيم, ، مكتوب في بيثون ، لديه نوع مماثل من premompiler.
استجابةً لتعليق Wai ، إليك حلًا ممتعًا وجدته. بادئ ذي بدء ، لشرح ما يفعله مرة أخرى ، لنفترض أن لديك الكود التالي:
definitions = Structure()
definitions.add_definition('f[x]', 'x*2')
definitions.add_definition('f[z]', 'some_function(z)')
definitions.add_definition('g.i', 'some_object[i].method(param=value)')
حيث تضيف إضافة تعريفات توصيل الجوانب اليسرى والجانبين اليمنى والقيام بأشياء قبيحة أخرى. الآن ، سيسمح نهج واحد (ليس جيدًا بالضرورة ، ولكن بالتأكيد ممتع) لكتابة الرمز أعلاه على النحو التالي:
@my_dsl
def definitions():
f[x] = x*2
f[z] = some_function(z)
g.i = some_object[i].method(param=value)
واطلب من بيثون القيام بمعظم التحليل تحت الغطاء. تعتمد الفكرة على البسيط exec <code> in <environment>
بيان ، ذكره إيان ، مع إضافة واحدة. وهي ، يجب أن يتم تعديل رمز الوظيفة قليلاً وتبديل جميع عمليات الوصول المتغيرة المحلية (LOAD_FAST) إلى الوصول المتغير من البيئة (LOAD_NAME).
يتم عرضه أسهل مما هو موضح: http://fouryears.eu/wp-content/uploads/pydsl/
هناك العديد من الحيل التي قد ترغب في القيام بها لجعلها عملية. على سبيل المثال ، في الكود المقدم في الرابط أعلاه ، لا يمكنك استخدام وظائف مدمجة وأنشأات اللغة مثل الحلقات وإذا كانت عبارات داخل وظيفة my_dsl. ومع ذلك ، يمكنك جعل هذه العمل ، عن طريق إضافة المزيد من السلوك إلى فئة ENV.
تحديث. هنا هو تفسير مطوّل أكثر قليلاً لنفس الشيء.