Snap! 中的 Python 解释器
文章目录
前言
Snap! 的 JavaScript function 简单, 灵活且强大, 我想模仿它构建 Python function。
我们之前在这篇文章里, 基于 CodeLab Adapter 构建了一个 Python function, 但我一直更想要一个除了浏览器之外没有任何依赖的 Python function。
使用场景
有了 Python function, 我们就可以在 Snap! 中获得:
- Python 生态中海量的第三方库(opencv-python, numpy…)
- Python 社区已经实现的各种算法(如 TheAlgorithms)
我们既可以在 Snap! 环境中获得 Python 生态积累的丰富工具, 又可以借助 Snap! 提升 Python 的活性(liveness)。
选择 Python 解释器
有多个 Python 解释器可运行在浏览器环境。 pyodide 是其中最强大的一个, 它允许我们使用 Python 生态大量的库, 如: opencv-python, Pillow, pyparsing, requests(最新的版本有问题), retrying, scikit-image 以及大量的科学计算相关的库(scipy, sympy, numpy, pandas)
实际上, pyodide 支持 任何纯 Python 实现的库(构建为 wheel), 使用了 C/C++ 代码的库(如 numpy)编译为 WebAssembly 也能够在 pyodide 中使用。
我们打算基于 pyodide 构建 Python function。
将 pyodide 引入 Snap!
目标
与我们上篇文章引入 mediapipe 库一样, 我们希望实现以下 2 个目标:
- 无需更新平台, 不需要开发者介入, 所有工作都在用户环境中完成(只是一个 Snap! 项目), 这意味着普通用户可以继续延伸这些能力。 (这是终端用户编程的一个例子)
- 可以充分利用 Snap! 的活性(liveness), 享受高效而愉快的开发体验。
由于一切都只是一个 Snap! 项目, 你可以随意调整它, 甚至使用其他的 Python 解释器替代 pyodide。
导入 pyodide
与 mediapipe 不同, pyodide 不是以模块的形式发布, 而是采用了更传统的全局引入。 我们可以使用 src_load(url)
引入它。
我们使用的 pyodide 版本为 0.25.1
, 它所对应的 Python 版本为 3.11
我们没有采用官方的 CDN, 而是将 pyodide 部署到了国内的 CDN 上。
demo
使用 requests 与网络交互
Snap! 有一个积木用于与网络交互, 但使用起来不是很便利, requests 的 API 设计得非常简单易用:
解析 XML
Snap! 没有内置 XML 库, Python 内置的 XML 库很好用,我们可以使用它从博客 rss 中提取信息。
这个例子展示了从博客获取最新 rss, 使用 python 的 xml 库解析它, 并将解析结果存入 posts 变量。之后将每篇博文显示为一个克隆体:
- 克隆体的外观是文章标题
- 鼠标在克隆体上悬停时,展示文章摘要
- 点击克隆体将打开文章链接
正则表达式
Snap! 缺少正则表达式相关的支持, 我们可以使用 python 的正则表达式模块增强它
在 Python 与 Snap! 之间传递列表
这个例子展示了如何在 Python 与 Snap! 之间传递列表(任意嵌套深度!)
列表几乎可以承载一切的数据结构.Python 和 Snap! 的列表功能都极其强大(Snap! 由于是 lisp, 列表处理能力比 python 更强大),现在这二者可以无缝互操作了。
图像、音频等多媒体数据全都可以表示为列表/矩阵,所有这些数据都能够在两个系统中随意流动了
一个典型的用例是, numpy 的所有能力都可以进入 Snap! 中, 反过来 Snap! 又可以为 numpy 提供 liveness 的增强
在 Python 与 Snap! 之间传递 json
对于复杂的数据结构, 可以考虑使用 json 传递. 也可以使用 json 来传递列表 (将列表作为一个 key 的 value ).
numpy demo
OpenCV demo
Pillow demo
社区第三方库
前边说到:
pyodide 支持 任何纯 Python 实现的库(构建为 wheel)
我们试着安装 furl 库, furl 用于方便地处理 url
pyodide 将从 pypi 上安装这个库(你也可以从指定的 url 安装自定义库)。
FAQ
如何保存状态
函数通常是无状态的, Python function 是一个函数。
如果你期望在多个函数调用中, 操作同一个状态变量, 可以把它存放在 window 全局变量里:
|
|
当然你也可以把它持久化存储在 localstorage 或 indexeddb, 这样即使刷新浏览器, 也还能找到这些状态。
如何调用蓝牙, 串口等浏览器 API
这个 demo(点击体验) 展示了如何使用 Python 连接 MicroBlocks 的蓝牙服务。
第三方库从哪儿加载?
load package
积木用于从网络加载第三方库, 它在背后使用了 micropip
可以从多种来源加载 python 包:
- pyodide 官方默认编译了这些 Python 库, 如果你加载这些库, 将默认从 pyodide 仓库里加载(我已经把它们放在了国内 CDN 上), 所以加载速度应该很快
- 其他的库默认从 pypi 安装
你也可以直接从 url 中加载库(你可以把它们托管在自己的服务器上), 如: https://wwj718.github.io/post/img/furl-2.1.3-py2.py3-none-any.whl
如何在 Snap! 和 Python 之间传递图像文件?
跨语言/系统传递媒体类信息(音频、图片)的常见做法是使用 base64 格式, 本文的例子采用的也是这种做法。
这个对话 中, ChatGPT 解释了这些这个格式编码的 png 格式。
你可以直接将 base64 编码的字符串(形如: data:image/png;base64,iVBORxxxx...
)粘贴到 Chrome 浏览器的输入栏中浏览图片:
如何将积木编译为 Python 代码?
通常而言,这不是个好想法, Snap! 的 lisp code 要更为强大(因为它与积木是同构的)。 如果确实需要编译为 Python,可以参考这个项目
如何使用更专业的 Python 编辑器?
在 Snap! 积木中写 Python 代码无法使用自动补全等功能, 如何使用更专业的 Python 编辑器?
一个思路是使用 iframe 库 引入 pyscrit(pyscrit-editor) 或者 marimo 这样更专业的 Python 编辑器, 然后使用 dynatalk-over-postmessage 进行消息传递
如何将其转化为标准的 Snap! 库?
目前 Python function 已成为了 Snap! 中文版 内置库.
更多细节参考 如何将其转化为标准的 Snap! 库?
结论
本文构建了与 JavaScript function 风格相同的 Python function 积木,一切都运行在浏览器中,没有任何外部依赖。 所有工作都在积木环境中完成,没有修改一行 Snap! 源代码。
最令人满意的是所有这些扩展性都掌握在普通用户自己的手里,无需开发人员的介入,也无需更新平台。一切都只是一个普通的 Snap! 项目!
参考
文章作者 种瓜
上次更新 2024-05-11