Hotdry.
compilers

Scratch到Python的浏览器端编译:AST转换与WebAssembly运行时

分析EktuPy项目如何通过AST转换器将Scratch-like同步API转换为异步Python代码,在浏览器中实现完整的Python编程环境,包括PyScript运行时、WebAssembly工具链与教育编程迁移策略。

教育编程的范式迁移:从积木到代码

Scratch 作为全球最受欢迎的儿童编程平台,通过拖拽积木的方式降低了编程门槛,但同时也制造了一个认知鸿沟:孩子们认为自己只是在 “移动积木”,而非真正的 “编程”。Kushal Das 在博客中分享了一个深刻的观察:他 11 岁的女儿在 Scratch 上创作了大量作品,却坚持认为自己不是程序员,因为她没有像成人那样 “敲代码”。

这种认知偏差催生了 EktuPy 项目 —— 一个在浏览器中运行 Scratch-like 游戏的 Python 库。项目的核心目标不是简单地复制 Scratch 功能,而是构建一座桥梁,让 Scratch 用户能够平滑过渡到文本编程。正如 Das 所言:“我长期思考如何将 Scratch 一代引导到 Python 编程,EktuPy 可能是解决方案。”

技术架构:三层编译栈的浏览器实现

EktuPy 的技术栈体现了现代 Web 编译技术的集大成:

1. PyScript 运行时层

项目基于 PyScript 构建,这是一个在浏览器中运行 Python 的平台。PyScript 底层依赖 PyOdide—— 将 CPython 编译为 WebAssembly 的技术。这意味着用户无需安装任何 Python 环境,直接在浏览器中就能执行完整的 Python 代码。

2. AST 转换器:同步到异步的魔法

最精妙的设计在于 AST(抽象语法树)转换器。为了让儿童编程体验保持简单,EktuPy 提供了类似 Scratch 的同步 API:

wait(2)  # 等待2秒
answer = ask("What's your name?")  # 同步询问
play_sound_until_done("meow")  # 播放声音直到结束

但在浏览器环境中,这些操作本质上是异步的。AST 转换器在编译阶段自动将这些同步调用转换为异步 / 等待模式:

# 转换后
await asyncio.sleep(2)
answer = await ask_async("What's your name?")
await play_sound_async("meow")

这种转换对用户完全透明,孩子们可以像写 Scratch 积木一样编写 Python 代码,而底层自动处理了异步复杂性。

3. 编辑器与工具链集成

EktuPy 使用 CodeMirror 作为代码编辑器,并通过 Astral 提供的 Ruff 和 ty 工具(编译为 WebAssembly)实现了 LSP(语言服务器协议)支持和代码检查。这意味着用户在浏览器中就能获得类似 IDE 的体验:语法高亮、自动补全、错误提示。

AST 转换机制深度解析

AST 转换器的实现涉及几个关键技术点:

模式匹配与重写

转换器首先解析 Python 代码生成 AST,然后遍历 AST 树寻找特定的模式。例如,对于wait()调用:

# 原始AST节点
Call(
    func=Name(id='wait', ctx=Load()),
    args=[Constant(value=2)],
    keywords=[]
)

# 转换后AST节点
Await(
    value=Call(
        func=Attribute(
            value=Name(id='asyncio', ctx=Load()),
            attr='sleep',
            ctx=Load()
        ),
        args=[Constant(value=2)],
        keywords=[]
    )
)

作用域分析与 async 函数包装

当检测到异步操作时,转换器需要确保包含这些操作的函数被标记为async。这需要作用域分析:

# 转换前
def game_loop():
    wait(1)
    move_sprite()

# 转换后
async def game_loop():
    await asyncio.sleep(1)
    move_sprite()

事件循环集成

EktuPy 维护一个中央事件循环,所有转换后的异步函数都在这个循环中调度。这确保了即使有多个精灵同时执行wait()操作,也不会阻塞整个应用。

WebAssembly 在浏览器 Python 运行时中的作用

PyOdide 的架构

PyOdide 将 CPython 解释器、Python 标准库以及科学计算库(如 NumPy、Pandas)编译为 WebAssembly 模块。在 EktuPy 中,这意味着:

  1. 完整的 Python 环境:用户可以使用几乎所有的 Python 标准库
  2. 性能优化:WebAssembly 提供了接近原生的执行速度
  3. 沙箱安全:代码在浏览器沙箱中运行,无法访问本地文件系统

内存管理挑战

WebAssembly 的线性内存模型与 Python 的垃圾回收机制需要适配。PyOdide 通过以下策略解决:

  • 使用 Emscripten 提供的内存管理工具
  • 实现 Python 对象到 JavaScript 对象的双向绑定
  • 优化频繁调用的 API 以减少跨语言调用开销

性能优化策略与监控要点

关键性能指标

  1. 初始加载时间:PyOdide 运行时约 5-8MB,需要优化加载策略
  2. AST 转换开销:对于大型项目,AST 遍历和重写可能成为瓶颈
  3. 内存使用:WebAssembly 内存限制通常为 4GB,需要监控 Python 堆使用

优化建议

# 1. 懒加载模块
# 避免一次性加载所有Python库
import ektupy.core as core  # 核心功能
# 按需加载其他模块

# 2. 缓存AST转换结果
# 对于不变的用户代码,缓存转换后的AST
transformation_cache = {}

# 3. 增量编译
# 只重新转换修改过的函数

监控配置

# 监控配置示例
monitoring:
  metrics:
    - name: "pyscript_load_time"
      threshold: "3000ms"  # 加载时间阈值
    - name: "ast_transformation_time"
      threshold: "100ms"   # AST转换时间
    - name: "memory_usage"
      threshold: "80%"     # 内存使用率
  
  alerts:
    - condition: "pyscript_load_time > 5000ms"
      action: "enable_progressive_loading"
    - condition: "memory_usage > 90%"
      action: "trigger_garbage_collection"

教育迁移路径设计

渐进式学习曲线

EktuPy 设计了从 Scratch 到 Python 的平滑过渡:

  1. 阶段一:熟悉环境

    • 使用与 Scratch 完全相同的坐标系统(960x720,中心 (0,0))
    • 相同的精灵控制 API(go_tochange_xsay
    • 相同的碰撞检测逻辑
  2. 阶段二:引入 Python 概念

    • 变量声明:从积木变量到 Python 变量
    • 循环结构:repeat积木到for/while循环
    • 条件判断:if积木到 Python 条件语句
  3. 阶段三:高级特性

    • 函数定义与模块化
    • 列表和字典数据结构
    • 面向对象编程基础

教学资源配套

EktuPy 为每个示例提供了配套教程,避免了儿童直接阅读技术文档的障碍。教程采用渐进式设计:

  • 第一步:复制粘贴示例代码
  • 第二步:修改参数观察变化
  • 第三步:组合多个示例创建新项目
  • 第四步:从头开始创作

部署架构与可扩展性

多租户支持

EktuPy 后端使用 Django 构建,支持多用户账户系统:

  • 用户注册与登录
  • 项目保存与版本管理
  • 公开分享与 remix 功能
  • 探索页面发现其他用户作品

扩展点设计

  1. 插件系统:允许教师添加自定义积木 / API
  2. 主题定制:支持不同年龄段的外观主题
  3. 集成评估:与学习管理系统(LMS)集成

部署参数建议

# 部署配置
DEPLOYMENT_CONFIG = {
    "pyscript": {
        "runtime": "pyodide-0.25.0",
        "preload_packages": ["ektupy", "math", "random"],
        "memory_limit": "512mb",
        "worker_count": 4  # Web Worker数量
    },
    "editor": {
        "theme": "scratch-light",  # 类似Scratch的浅色主题
        "autocomplete": True,
        "linting": True,
        "keyboard_shortcuts": "scratch-style"
    },
    "performance": {
        "cache_ttl": 3600,  # 缓存1小时
        "compress_ast": True,
        "lazy_load_graphics": True
    }
}

挑战与未来方向

当前限制

  1. 性能开销:相比原生 Python,PyOdide 有约 2-5 倍的性能损失
  2. 库兼容性:并非所有 Python 库都能在 WebAssembly 中运行
  3. 调试困难:浏览器中的 Python 调试工具链尚不完善

技术演进方向

  1. WASI 支持:随着 WebAssembly System Interface 的成熟,可以访问更多系统资源
  2. JIT 编译:WebAssembly JIT 编译器可能进一步提升性能
  3. 多语言支持:扩展到其他教育语言(如 JavaScript、Lua)

教育生态建设

EktuPy 的成功不仅在于技术实现,更在于构建了一个完整的教育编程生态:

  • 教师培训材料
  • 课程计划模板
  • 学生作品展示平台
  • 家长指导手册

结语:编译技术的人文价值

EktuPy 项目展示了编译技术在教育领域的创新应用。通过 AST 转换器,它不仅仅是将一种语言转换为另一种语言,更是将一种思维方式(积木式思维)平滑过渡到另一种思维方式(文本编程思维)。

正如 Kushal Das 在博客中所说:“这个项目离不开之前的所有工作,包括 Scratch、CodeMirror 编辑器、PyScript/PyOdide,以及更大的 Python 社区。” 这提醒我们,技术创新的价值往往在于连接与转化,而非从零创造。

对于教育技术开发者而言,EktuPy 提供了一个可复用的模式:如何通过编译技术降低学习曲线,如何通过工具设计引导认知发展,以及如何通过社区建设巩固学习成果。在这个意义上,EktuPy 不仅是一个技术项目,更是一个教育实验,一个关于如何让下一代从 “移动积木者” 成长为 “代码创作者” 的实践探索。


资料来源

  1. Kushal Das 博客:https://kushaldas.in - EktuPy 项目设计理念与开发背景
  2. EktuPy 文档:https://docs.ektupy.org/ - API 设计与技术实现细节
查看归档