Snap! 使用笔记: Python function(CodeLab Adapter版)
文章目录
制造伟大的、可成长的系统的关键是设计出模块之间的沟通机制,而不是关于内部属性和行为应该是什么。 – Alan Kay
前言
提示
更推荐使用基于 pyodide 的 Python function, 它不依赖于 CodeLab Adapter。
Snap! 的 JavaScript function 设计得极为出色, 仅通过一个积木就将浏览器的所有能力带给了 Snap! 。 它给了我巨大冲击。 关于这点我们在 Snap! 使用笔记: JavaScript function 做了讨论。
目标
为了将 Snap! 用作日常计算/编程环境,需要突破浏览器的限制,使其得到操作系统的所有能力。我打算沿着Jens Mönig(Snap! 创始人)的设计思路, 来做这件事。
先前的工作
之前我基于 CodeLab Adapter 的消息系统为 Snap! 接入 Python 来达成这个目标。想法是: 如果我们能够完成 Snap! 与 Python 的互操作,就能在 Snap! 中完成任何工作, 因为 Python 解释器封装了底层操作系统的所有接口(Python 通常被认为是更好的 Bash), 如此一来我们就可以在 Snap! 里进行 系统 io 编程、管理远程服务器、渗透测试、与其他进程互操作…
以下是一些我渴望在 Snap! 中获得的系统能力:
- 处理文件/文件夹/路径
- Python 库: pathlib
- 以 GUI 的方式打开文件/软件/目录:
from codelab_adapter.utils import open_path_in_system_file_manager
- 调用命令行工具
- Python 库: subprocess、sh
- 与网络交互
- Python 库:
- HTTP: requests
- SSH: Paramikossh
- TCP、UDP、ZeroMQ…
- Python 库:
- 接管鼠标键盘
- Python 库: pyautogui
- …
由于接入了 Python, 我们不仅得到了与浏览器之外的系统交互的能力, 还得到了 Python 的巨大生态库:
- 计算机视觉
- OpenCV
- 神经网络
- PyTorch、TensorFlow
- 各类机器人驱动库…
最初我们在 CodeLab Scratch 里实现了这些想法, 它体现为 Python 插件:
我最近将其移植到了 Snap! 中:
所以它的使用方式和最初的文档记录的使用方式,完全一致:
重构
先前的插件存在一个问题: 不够 lively。 它类似于Snap! 使用笔记: JavaScript function里提到的使用 primitive 扩展 Snap! : 需要先在 Jupyterlab 中编写 Python 代码, 然后重启 extension_python 插件, 之后在 Snap! 里调用它, 虽然无需刷新 Snap! 页面,但切换到 Jupyterlab、重启插件, 依然打断我的思路。
由于受到 JavaScript function 的强烈冲击, 我打算以类似的方式来做这件事: 将操作系统的所有能力带给 Snap! (通过与 Python 互操作)。
Python function 插件
这些想法的最终产物是 Python function 插件:
Python function 并没有设计成单个积木, 而是设计成了一个插件, 里边多了一些辅助类积木。
怎么用?
为了使用 Python function 积木, 你需要做 3 件事(与JavaScript function一致):
- 打开权限
1.1. 开启 CodeLab Adapater
1.2. 开启 extension_python 插件
1.3. 设置 token(出于安全考虑) - 编写 Python 函数
- 调用 Python 函数
1. 打开权限
- 1.1. 开启 CodeLab Adapater
下载最新的 CodeLab Adapater, 并运行它。
CodeLab Adapater 正常运行之后,你会在 CodeLab Snap! 页面里看到这个绿色的圆点(和 CodeLab Scratch 类似)
如果 CodeLab Adapater 没有启动或出了问题,这个原点就会是灰色的。
- 1.2. 开启 extension_python 插件
- 1.3. 设置 token (出于安全考虑)
2. 编写 Python 函数
在 Python function 插件中找到 Python function 积木。上图编写了一个匿名函数, 参数是 path, 函数体是打开它(可能是目录、文件或系统里的 GUI 软件)。
附上相应 Python 代码
|
|
3. 调用 Python 函数
接下来我们就可以调用它。
和 JavaScript function 一样, 有两个积木可用于执行 JavaScript function:
- 运行(没有返回值)
- 调用(有返回值)
这两个积木都内置在 Python function 插件里(底层实现与控制组里的同名积木并不相同)
更复杂的例子, 可以右键查看examples积木的定义:
我的用例
与 Snap! 使用笔记: JavaScript function 提到的用例类似。
我不仅使用 Python function 来增强 Snap!, 也借助 Snap! 的个人计算环境来探索 Python 以及背后的操作系统。
此外有一个有趣的用例是, 使用 Python function 来执行 Codification 示例自动生成的 Python 代码:
一些技巧
安装第三方库
我喜欢使用 sh 库来在 Python 里使用 linux 工具箱。
CodeLab Adapter 没有内置这会库, 但你可以自行安装它:
安装完成之后,就可以使用它了:
按行分割,将它们转化为列表:
由于 Snap! 从 Lisp 那里继承了处理列表的强大能力, 又从 Smalltalk 那里继承了 liveness 的特质, 现在你就可以以 lively 方式随意摆弄它们了!
类型转化
从 Snap! 中调用 Python 函数时, Snap! 的数据类型会自动转化为 Python 类型, 随意你可以随意使用内置积木调用 Python 函数。
从 Python 中返回的简单数据类型(字符串、数字), 会自动转化为 Snap! 数据类型. 当你想将复杂数据结构 return 到 Snap! 里时,建议使用 json 序列化你的数据(examples积木里有例子), 然后在 Snap 中处理 json 即可。
全局变量
这些匿名函数转瞬即逝,有时,你希望让计算的结果贮存在全局空间,可以在不同函数里读写它
这使得许多事情变得简单
已知限制
extension_python 以线程的方式启动, 有一些库需要在独立进程里启动(非常罕见)。如果你遇到这类问题,可以发邮件跟我讨论。
由于 extension_python 是开源的,你可以自行修补它,使用一些 hack 技巧,总是可以绕过限制。
我目前还没有遇到过这些限制。
编程风格
尽可能少地引入 primitive 是 Smalltalk 社区编写 primitive 遵循的风格, 关于 primitive 的 big ideas, 我在 MicroBlocks 与 Snap!的互操作里(视频)提到过一些 – Snap! 使用笔记: JavaScript function
当我在 Snap! 中使用 Python 时,我也遵循类似的风格, 尽可能少地引入底层的能力(除非必要), 尽可能多地使用 Snap! 积木来编程。
我希望涉及 Python 的部分足够小, 一般而言是为了补充浏览器没有的能力(本地系统功能、网络),我将多数的计算过程放在 Snap! 里,借助 Snap! 这样的个人计算环境,我可以直观触摸到数据,以及充分利用个人计算环境的其他杠杆力量。
未来
Python function 的 Python 代码,最终运行在 CodeLab Adapater 内置的 Python 解释器里。未来我们也可能支持基于 WebAssembly 的 Python 解释器(如Pyodide)。
后记
递归是我之前最恐惧的领域之一,但在 Snap! 里玩耍它几乎是一种消遣。 因为你可以"看见"并把玩程序与数据结构。
这一段代码,使用递归将 Snap! 数据类型递归转化为 Python 数据类型, 它可以处理任意深度的 Snap! 列表。
参考
文章作者 种瓜
上次更新 2023-03-01