2012-02-24 47 views
11

以下是使用ASTsymtable 包一個Python代碼段。我試圖解析代碼並檢查類型。但我 不明白如何遍歷對象去到被引用的實際變量 。的Python AST包:遍歷對象層次

下面的代碼實現了一個NodeVisitor,並且一個函數被呈現給編譯器並由編譯器解析並且ast走了。被分析的函數(eval_types)傳遞一對對象。

下面是組成該示例的代碼塊。我已經爲每個塊添加了一些評論。爲了運行代碼,需要重新組裝「塊」。

這是一個導入和一個函數來取消解壓縮代碼塊。

import inspect 
import ast 
import symtable 
from tokenize import generate_tokens, untokenize, INDENT 
from cStringIO import StringIO 

# _dedent borrowed from the myhdl package (www.myhdl.org) 
def _dedent(s): 
    """Dedent python code string.""" 

    result = [t[:2] for t in generate_tokens(StringIO(s).readline)] 
    # set initial indent to 0 if any 
    if result[0][0] == INDENT: 
     result[0] = (INDENT, '') 
    return untokenize(result) 

以下是節點訪問者,它具有通用的未處理和名稱訪問器重載。

class NodeVisitor(ast.NodeVisitor): 
    def __init__(self, SymbolTable): 
     self.symtable = SymbolTable 
     for child in SymbolTable.get_children(): 
      self.symtable = child 
      print(child.get_symbols()) 

    def _visit_children(self, node): 
     """Determine if the node has children and visit""" 
     for _, value in ast.iter_fields(node): 
      if isinstance(value, list): 
       for item in value: 
        if isinstance(item, ast.AST): 
         print(' visit item %s' % (type(item).__name__)) 
         self.visit(item) 

      elif isinstance(value, ast.AST): 
       print(' visit value %s' % (type(value).__name__)) 
       self.visit(value) 

    def generic_visit(self, node): 
     print(type(node).__name__) 
     self._visit_children(node) 

    def visit_Name(self, node): 
     print(' variable %s type %s' % (node.id, 
             self.symtable.lookup(node.id))) 
     print(dir(self.symtable.lookup(node.id))) 

以下是一些簡單的類,將在函數中使用,將用AST進行分析和分析。

class MyObj(object): 
    def __init__(self): 
     self.val = None 

class MyObjFloat(object): 
    def __init__(self): 
     self.x = 1. 

class MyObjInt(object): 
    def __init__(self): 
     self.x = 1 

class MyObjObj(object): 
    def __init__(self): 
     self.xi = MyObjInt() 
     self.xf = MyObjFloat() 

以下是測試函數,eval_types函數是將用AST分析的函數。

def testFunc(x,y,xo,z): 

    def eval_types(): 
     z.val = x + y + xo.xi.x + xo.xf.x 

    return eval_types 

執行該示例的代碼,編譯該函數並進行分析。

if __name__ == '__main__': 
    z = MyObj() 
    print(z.val) 
    f = testFunc(1, 2, MyObjObj(), z) 
    f() 
    print(z.val) 
    s = inspect.getsource(f) 
    s = _dedent(s) 
    print(type(s)) 
    print(s) 

    SymbolTable = symtable.symtable(s,'string','exec') 
    tree = ast.parse(s) 
    v = NodeVisitor(SymbolTable) 
    v.visit(tree) 

以下是輸出到名字訪問的示例。

Module 
    visit item FunctionDef 
FunctionDef 
    visit value arguments 
arguments 
    visit item Assign 
Assign 
    visit item Attribute 
Attribute 
    visit value Name 
    variable z type <symbol 'z'> 
['_Symbol__flags', '_Symbol__name', '_Symbol__namespaces', 
'_Symbol__scope', '__class__', '__delattr__', '__dict__', 
'__doc__', '__format__', '__getattribute__', '__hash__', 
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', 
'__repr__', '__setattr__', '__sizeof__', '__str__', 
'__subclasshook__', '__weakref__', 'get_name', 'get_namespace', 
'get_namespaces', 'is_assigned', 'is_declared_global', 
'is_free', 'is_global', 'is_imported', 'is_local', 
'is_namespace', 'is_parameter', 'is_referenced'] 

創建節點遊客似乎並不壞,但我無法弄清楚 如何遍歷對象層次。在一般情況下,被訪問的變量 可以被深埋在對象中。如何獲得從ast訪問者訪問的實際變量?我只看到一個對象位於節點上,但沒有附加信息是什麼結果變量訪問。

+1

不知道我按照你想要的。是不是你找不到「.val」?你有沒有嘗試在該節點下遞歸?這是很長一段時間,因爲我使用這個,但我似乎記得你需要,在你的情況下,從visit_name調用visit_children。 – 2012-03-05 04:20:13

+0

@andrewcooke感謝您的評論!是的,我想繼續解析對象(z.val,xo.xi.x,xo.xf.x)並確定有關語句中使用的實際變量的更多信息。簡單的情況是,z = x + y + xo_xi_x + xo_xf_x,其中的變量沒有嵌入(掩埋)在對象中,變量的屬性/屬性/類型可以在上面的代碼片段中確定。我將嘗試在這些類型的節點上顯式調用visit_children。再次感謝。 – 2012-03-05 11:39:29

+0

製作小型(慢速)進度,發展對AST包裝的理解。對於這個例子,對象「屬性」將沿着** visit_Attribute **行進。現在我只需要確定** visit_Name **何時有一個對象(有孩子?),然後在visit_Attribute中完成並向後工作(備份樹)。 – 2012-03-07 12:05:48

回答

2

我不知道你是否仍在尋找這個,但看起來你只需要添加一個visit_Attribute並向後遍歷。如果您添加到您的示例:

def visit_Attribute(self, node): 
    print(' attribute %s' % node.attr) 
    self._visit_children(node) 

然後輸出xo.xf.x是:

Add 
    visit value Attribute 
    attribute x 
    visit value Attribute 
    attribute xf 
    visit value Name 
    variable xo type <symbol 'xo'> 
    visit value Load 

根據要與此做什麼,你只需要存儲在列表中的屬性,直到遇到一個Name,然後反轉它們。