← 返回 Day 96 正文

Day 96 · 图解

Day 096 — AST 与代码分析 — 图解

1. Python 代码执行流程

┌──────────────────────────────────────────────────────────────┐
│                    Python 代码执行流程                         │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  源代码 (.py)                                                 │
│     │                                                        │
│     ▼                                                        │
│  ┌─────────────┐                                             │
│  │  Tokenizer   │  词法分析:把代码拆成 Token                  │
│  │  词法分析器   │  "x = 1 + 2" → NAME(x) EQUAL NUMBER(1)   │
│  └──────┬──────┘         PLUS NUMBER(2)                     │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────┐                                             │
│  │   Parser     │  语法分析:Token → AST                     │
│  │  语法分析器   │  检查语法是否合法                           │
│  └──────┬──────┘                                             │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────────────────────┐                     │
│  │   AST(抽象语法树)                  │  ← 我们操作的层次   │
│  │                                     │                     │
│  │  Module                              │                     │
│  │  └── Assign                          │                     │
│  │      ├── targets: [Name('x')]        │                     │
│  │      └── value: BinOp                │                     │
│  │          ├── left: Constant(1)       │                     │
│  │          ├── op: Add()               │                     │
│  │          └── right: Constant(2)      │                     │
│  └──────────────┬──────────────────────┘                     │
│                 │                                            │
│                 ▼                                            │
│  ┌─────────────┐                                             │
│  │  Compiler    │  编译 AST → 字节码                         │
│  │  编译器       │  (bytecode / .pyc)                        │
│  └──────┬──────┘                                             │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────┐                                             │
│  │  Python VM   │  执行字节码                                 │
│  │  虚拟机       │                                            │
│  └─────────────┘                                             │
│                                                              │
└──────────────────────────────────────────────────────────────┘

2. AST 节点类型层次

AST (基类)
│
├── mod (模块)
│   ├── Module         # 顶层模块
│   ├── Expression     # 单个表达式
│   └── Interactive    # 交互式输入
│
├── stmt (语句) ──────────── 出现在 body 列表中
│   ├── FunctionDef    # def foo():
│   ├── AsyncFunctionDef
│   ├── ClassDef       # class Foo:
│   ├── Return         # return x
│   ├── Delete         # del x
│   ├── Assign         # x = 1
│   ├── AugAssign      # x += 1
│   ├── AnnAssign      # x: int = 1
│   ├── For / While    # 循环
│   ├── If             # 条件
│   ├── With           # 上下文管理器
│   ├── Raise          # raise
│   ├── Try            # try/except/finally
│   ├── Assert         # assert
│   ├── Import         # import
│   ├── Global / Nonlocal
│   ├── Expr           # 表达式语句
│   └── Pass / Break / Continue
│
├── expr (表达式) ────────── 出现在值/条件等位置
│   ├── Name           # 变量名
│   ├── Constant       # 字面量 (3.8+)
│   ├── BinOp          # a + b
│   ├── UnaryOp        # -x, not x
│   ├── BoolOp         # a and b
│   ├── Compare        # a > b
│   ├── Call           # func(args)
│   ├── Attribute      # obj.attr
│   ├── Subscript      # obj[key]
│   ├── starred        # *args
│   ├── List / Tuple / Set / Dict
│   ├── ListComp / SetComp / DictComp / GeneratorExp
│   ├── Lambda         # lambda x: ...
│   ├── IfExp          # a if cond else b
│   └── Await / Yield / JoinedStr / FormattedValue
│
├── keyword (关键字参数)
├── alias (import 别名)
├── arguments (函数参数)
├── arg (单个参数)
└── operator (运算符)
    ├── Add, Sub, Mult, Div, Mod, Pow
    ├── LShift, RShift, BitOr, BitXor, BitAnd
    ├── MatMult
    └── And, Or, Not

3. NodeVisitor 遍历流程

源代码
  │
  ▼
ast.parse() → AST Root
  │
  ▼
Visitor.visit(Root)
  │
  ├── visit_语句类型(node)
  │     │
  │     ├── 检查/处理当前节点
  │     │
  │     └── generic_visit(node)  ← 遍历子节点
  │           │
  │           ├── visit_子节点类型(child1)
  │           │     └── ...
  │           ├── visit_子节点类型(child2)
  │           │     └── ...
  │           └── visit_子节点类型(child3)
  │                 └── ...
  │
  └── 返回收集的结果

4. Linter 分析流程

┌────────────────────────────────────────────────────────┐
│                  自定义 Linter 工作流程                   │
├────────────────────────────────────────────────────────┤
│                                                        │
│  1. 读取源文件                                         │
│     source = open("file.py").read()                   │
│     │                                                  │
│     ▼                                                  │
│  2. 解析为 AST                                         │
│     tree = ast.parse(source)                           │
│     │                                                  │
│     ▼                                                  │
│  3. 创建 Linter 实例                                   │
│     linter = MyLinter()                                │
│     │                                                  │
│     ▼                                                  │
│  4. 遍历 AST 触发规则检查                               │
│     linter.visit(tree)                                 │
│     │                                                  │
│     ├── visit_FunctionDef  → 检查 W001/W002/W003       │
│     ├── visit_Assign       → 检查 W004                 │
│     ├── visit_ClassDef     → 检查 W005                 │
│     └── visit_ExceptHandler → 检查 E001                │
│     │                                                  │
│     ▼                                                  │
│  5. 收集告警                                           │
│     linter.warnings → [LintWarning, ...]              │
│     │                                                  │
│     ▼                                                  │
│  6. 格式化输出                                         │
│     ├── L3:10 [W001] 函数名应使用 snake_case           │
│     ├── L15:5 [W002] 函数超过 50 行                    │
│     └── L28:0 [E001] 不应使用裸 except                 │
│                                                        │
└────────────────────────────────────────────────────────┘

5. AST 变换 vs NodeVisitor 对比

NodeVisitor(只读)          NodeTransformer(可修改)
─────────────────          ────────────────────────
                           返回修改后的节点
  visit_Xxx(node):           visit_Xxx(node):
    # 读取信息                  # 修改节点
    self.data.append(...)        node.value = new_value
    # 不修改节点                 return node  ← 关键!
                               # 或返回新创建的节点

6. 安全执行对比

eval(user_input)               ast.literal_eval(user_input)
─────────────────              ──────────────────────────
❌ 危险!可执行任意代码           ✅ 安全!只解析字面量
                               

eval("__import__('os').system('ls')")  # ✅ 执行了!
eval("os.system('ls')")                # ✅ 执行了!

ast.literal_eval("{'a': 1}")          # ✅ 正确解析
ast.literal_eval("1 + 2")             # ❌ ValueError(表达式不允许)