前言

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 个目标:

  1. 无需更新平台, 不需要开发者介入, 所有工作都在用户环境中完成(只是一个 Snap! 项目), 这意味着普通用户可以继续延伸这些能力。 (这是终端用户编程的一个例子)
  1. 可以充分利用 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 设计得非常简单易用:

requests demo

正则表达式

Snap! 缺少正则表达式相关的支持, 我们可以使用 python 的正则表达式模块增强它

搜索电话号码 demo

在 Python 与 Snap! 之间传递列表

这个例子展示了如何在 Python 与 Snap! 之间传递列表(任意嵌套深度!)

列表几乎可以承载一切的数据结构.Python 和 Snap! 的列表功能都极其强大(Snap! 由于是 lisp, 列表处理能力比 python 更强大),现在这二者可以无缝互操作了。

图像、音频等多媒体数据全都可以表示为列表/矩阵,所有这些数据都能够在两个系统中随意流动了

一个典型的用例是, numpy 的所有能力都可以进入 Snap! 中, 反过来 Snap! 又可以为 numpy 提供 liveness 的增强

传递列表 demo

numpy demo

numpy demo

OpenCV demo

OpenCV demo

Pillow demo

Pillow demo

社区第三方库

前边说到:

pyodide 支持 任何纯 Python 实现的库(构建为 wheel)

我们试着安装 furl 库, furl 用于方便地处理 url

pyodide 将从 pypi 上安装这个库(你也可以从指定的 url 安装自定义库)。

furl demo

FAQ

如何保存状态

函数通常是无状态的, Python function 是一个函数。

如果你期望在多个函数调用中, 操作同一个状态变量, 可以把它存放在 window 全局变量里:

1
2
3
import js
js.window._result = "hello"
# 可在 devtool 中访问: console.log(window.a)

当然你也可以把它持久化存储在 localstorage 或 indexeddb, 这样即使刷新浏览器, 也还能找到这些状态。

如何调用蓝牙, 串口等浏览器 API

目前 pyodide 还无法调用这个 API, 因为 pyodide 的 Python 解释器运行在 Worker 中, 收到与 Worker 相同的权限限制。

但 基于 pyodide 的 pyscript 解决这些问题, 未来或许会合并到 pyodide 中。

结论

本文构建了与 JavaScript function 风格相同的 Python function 积木,一切都运行在浏览器中,没有任何外部依赖。 所有工作都在积木环境中完成,没有修改一行 Snap! 源代码。

最令人满意的是所有这些扩展性都掌握在普通用户自己的手里,无需开发人员的介入,也无需更新平台。一切都只是一个普通的 Snap! 项目!

参考