مثال بسيط على كيفية استخدام AST.NODEVISTOR؟
-
19-09-2019 - |
سؤال
هل لدى أي شخص مثال بسيط باستخدام AST.NODEVITITOR للمشي شجرة بناء الجملة مجردة في بيثون 2.6؟ الفرق بين الزيارة والأعمار_Visit غير واضح بالنسبة لي، ولا يمكنني العثور على أي مثال باستخدام Google Codesearch أو Google عادي.
المحلول
ast.visit
- ما لم تتجاوزه في فئة فرعية، بالطبع - عند دعا لزيارة ast.Node
من صنف foo
, ، المكالمات self.visit_foo
إذا كانت هذه الطريقة موجودة، وإلا self.generic_visit
. وبعد هذا الأخير، مرة أخرى في تنفيذها في الفصل ast
نفسه، فقط يدعو self.visit
على كل عقدة الطفل (ويعمل أي إجراء آخر).
لذلك، اعتبر، على سبيل المثال:
>>> class v(ast.NodeVisitor):
... def generic_visit(self, node):
... print type(node).__name__
... ast.NodeVisitor.generic_visit(self, node)
...
هنا، نحن نتجاوز generic_visit
لطباعة اسم الفصل، ولكن أيضا الدعوة إلى الفئة الأساسية (حتى تتم زيارة جميع الأطفال أيضا). لذلك على سبيل المثال ...:
>>> x = v()
>>> t = ast.parse('d[x] += v[y, x]')
>>> x.visit(t)
تنبعث منها:
Module
AugAssign
Subscript
Name
Load
Index
Name
Load
Store
Add
Subscript
Name
Load
Index
Tuple
Name
Load
Name
Load
Load
Load
لكن لنفترض أننا لم نهتم بعقد حمل (وأطفال منها - إذا كان لديهم أي ؛-). ثم طريقة بسيطة للتعامل مع ذلك قد تكون، على سبيل المثال:
>>> class w(v):
... def visit_Load(self, node): pass
...
الآن عندما نستمر عقدة الحمل، visit
إفرازات، وليس ل generic_visit
أي أكثر، ولكن لدينا الجديد visit_Load
... التي لا تفعل أي شيء على الإطلاق. وبالتالي:
>>> y = w()
>>> y.visit(t)
Module
AugAssign
Subscript
Name
Index
Name
Store
Add
Subscript
Name
Index
Tuple
Name
Name
أو لنفترض أننا أردنا أيضا أن نرى الأسماء الفعلية لعقد الاسم؛ ومن بعد...:
>>> class z(v):
... def visit_Name(self, node): print 'Name:', node.id
...
>>> z().visit(t)
Module
AugAssign
Subscript
Name: d
Index
Name: x
Store
Add
Subscript
Name: v
Index
Tuple
Name: y
Name: x
Load
Load
لكن nodeevisitor هو فئة لأن هذا يتيح لها تخزين المعلومات أثناء الزيارة. لنفترض أن كل ما نريده هو مجموعة الأسماء في "وحدة". ثم لا نحتاج إلى تجاوز generic_visit
أي أكثر، ولكن ...:
>>> class allnames(ast.NodeVisitor):
... def visit_Module(self, node):
... self.names = set()
... self.generic_visit(node)
... print sorted(self.names)
... def visit_Name(self, node):
... self.names.add(node.id)
...
>>> allnames().visit(t)
['d', 'v', 'x', 'y']
هذا النوع من الأشياء هو حالة استخدام أكثر نموذجية من تلك التي تتطلب تجاوزات generic_visit
- عادة، أنت مهتم فقط ببعض أنواع العقد، كما نحن هنا في الوحدة النمطية والاسم، لذلك يمكننا تجاوز visit_Module
و visit_Name
واترك AST visit
هل الإرسال نيابة عنا.
نصائح أخرى
النظر في الكود في AST.PY. ليس من الصعب نسخ معجون ولف جهاز Walker الخاص بك. على سبيل المثال
import ast
def str_node(node):
if isinstance(node, ast.AST):
fields = [(name, str_node(val)) for name, val in ast.iter_fields(node) if name not in ('left', 'right')]
rv = '%s(%s' % (node.__class__.__name__, ', '.join('%s=%s' % field for field in fields))
return rv + ')'
else:
return repr(node)
def ast_visit(node, level=0):
print(' ' * level + str_node(node))
for field, value in ast.iter_fields(node):
if isinstance(value, list):
for item in value:
if isinstance(item, ast.AST):
ast_visit(item, level=level+1)
elif isinstance(value, ast.AST):
ast_visit(value, level=level+1)
ast_visit(ast.parse('a + b'))
يطبع خارج
Module(body=[<_ast.Expr object at 0x02808510>])
Expr(value=BinOp(op=Add()))
BinOp(op=Add())
Name(id='a', ctx=Load())
Load()
Add()
Name(id='b', ctx=Load())
Load()
generic_visit
يسمى عندما لا يمكن العثور على زائر مخصص (أي visit_name). إليك قطعة من التعليمات البرمجية كتبت مؤخرا مع AST.NODEVISTOR: https://bitbucket.org/pypy/pypy/src/6df19fd2b6df6058daf162100cf7ee4521de5259/py/_code/_assertionnew.py؟at=default&fileviewer=file-View-View-Default. يفسر عقد AST للحصول على معلومات تصحيح الأخطاء حول بعضها ويندرج generic_visit
عند تنفيذ تنفيذ خاص.