2024年在浏览器中用Python做数据科学:Pyodide

从主流的Python 解释器(CPython)下载源码,和科学计算包(NumPy等)

引入很小的改变来使得它们能够在新环境中工作

使用emscripten的编译器将它们编译为WebAssembly

如果你直接将WebAssembly加载进入浏览器,你会发现事情与你直接在你的操作系统上运行Python解释器有非常明显的不同。例如,Web浏览器没有一个文件系统(用于加载和存储文件的地方)。幸运的是,emscripten提供了一个虚拟的文件系统,其使用JavaScript编写并能够被Python解释器使用。这些虚拟的“文件”默认保存在浏览器标签对应的内存上,当你导航离开该页面时,它们就会消失。(emscripten也提供了让该文件系统能够将数据存储在浏览器的持续性本地存储上的方式,但是Pyodide没有使用该功能。)

通过模拟出标准计算环境的文件系统以及其它一些特性,emscripten使得将现存的项目转移到Web浏览器上成为可能而且令人惊喜的是只需要少量的修改。(某一天,我们也许会转移到使用WASI来作为系统模拟层,但是目前来说emscripten是一个更成熟和完整的选择)。

总的来说,为了能够在你的浏览器中加载Pyodide,你需要下载:

已经编译到WebAssembly的Python解释器

一系列由emscripten提供的JavaScript用于提供系统模拟

一个打包好含有Python解释器所需要的所有的文件的文件系统,最重要的就是Python标准库

这些文件可能会非常大: Python本身就是21MB,NumPy是7MB等等。幸运的是,这些包只需要被下载一次,之后他们会被存储在浏览器的缓存中。

先后完成这些步骤以后,Python解释器就能够访问它的标准库中的文件,启动,以及开始运行用户代码。

哪些能工作,哪些又不能

我们运行了CPython的单元测试作为Pyodide的持续性测试的一部分来理解哪些Python的特性可以工作哪些又不可以工作。一些功能,比如线程现在还不能工作,但是通过最新可获取的WebAssembly 线程,我们应该在不远的将来就能加入支持。

其它特性,例如 底层网络sockets由于浏览器的安全沙盒几乎不可能实现。很抱歉需要告诉你,你希望在你的Web浏览器中运行一个Pythonminecraft 服务器的希望还有很长一段路要走。然而,你可以通过浏览器的API来通过网络获取数据(更多细节在后面的内容)。

它有多快?

在JavaScript虚拟机中运行Python解释器具有性能损失,但是该损失实际上是令人吃惊的小—在我们的基准测试中,相比于原生的速度在Firefox上要慢1到12倍在Chrome上要慢1到16倍。经验表明,这对于交互式开发来说已经足够使用了。

值得注意的是,在Python中有大量的内部循环的代码的速度趋向于以更大的系数慢于那些依赖于NumPy进行内部循环的代码。下面是在同一个硬件上与原生运行相比,在Firefox和Chrome上运行各种纯Python和Numpy基准测试的结果。

在Python和JavaScript之间进行交互

如果所有Pyodide能做的就只是运行Python代码并写出到标准输出上,它将会增长成为一个不错的很酷的技巧,但是不会成为一个用于实际工作的实用工具。真正的力量源于它与浏览器API以及其它运行在浏览器中的JavaScript库交互的能力。由于我们已经将Python解释器编译为了WebAssembly,它也与JavaScript端具有深度的交互。

Pyodide会在许多Python与JavaScript之间的内建数据类型之间进行隐式转换。其中一些转换时很直接明显的,但如往常一样,那就是很有趣的极端情况。

Python将字典和对象实例作为不同的类型来对待。字典是从键到值得映射。另一方面,对象通常拥有一些能够作用于该对象的方法。在JavaScript中,这两个概念被合并到一个单一类型对象。(是的,这里我进行了过度简化来使描述易于理解)

没有对开发者使用JavaScript对象的意图进行真正的理解,要去有效的区分是否应该将其转换为Python字典或者对象将会是不可能的。因此,我们不得不使用一个代理和让”鸭子类型“来处理这个情形。

代理是一个围绕在其它语言中的一个变量的包装器。相比于简单地在JavaScript中读取变量并重写为Python的数据结构(就如基本类型的做法一样),代理坚持了原始的JavaScript变量,并在需要时对它调用方法。这意味着任何JavaScript变量,不管有多么定制化,都是完全能够从Python访问的。代理在其它方面也能工作。

鸭子类型是一个原则,而不是询问一个变量“你是一只鸭子吗?”你问它“你走路像鸭子吗?”以及“你叫得像鸭子吗?”并从中推断它很有可能是一只鸭子,或者至少会像鸭子一样工作。这允许Pyodide来延迟做出如何转换JavaScript对象的决定:它将其包装在一个代理中并让使用它的代码来决定如何处理。当然,这并非总是能工作,鸭子可能实际上是一只兔子。因此,Pyodide也提供了方法来显式地处理这些转换。

正是这样级别的紧密集成允许了用户能够在Python中处理它们的数据,并将其发送给JavaScript来可视化。例如,在我们的Hipster Band Finder 展示中,我们表达了如何在Python的Pandas中加载和分析数据,然后将其发送给JavaScript的Plotly 来进行可视化。

访问Web API和DOM

代理也被证明是访问Web API以及浏览器所提供的使API工作的函数集的关键。例如,很大部分Web API是在document对象上。你可以通过如下方式从Python中获取:

这会将document对象作为一个代理从JavaScript端导入到Python端。你可以开始从Python中对其调用方法:

所有这些都通过能够动态查询document对象的代理来完成。Pyodide不需要包含一个浏览器所有的全部API的完整列表。

当然,直接使用Web API并不总是最Python化或者用户友好型的工作方式。更棒的方式要等到对Web API的用户友好型的Python包装器的出现,就像jQuery和其它的库如何使得Web API能够从JavaScript中使用一样。 如果你感兴趣于为这个想法来工作,请告知我们!

多维数组

有些数据类型是特定对数据科学有用的,而Pyodide对它们也有专门的支持。多维数组是具有相同类型的值(通常是数值)的集合。它们倾向于非常大,并且知道每个元素都是同样的类型相对于能够存储任何类型的Python list和JavaScriptArrays来说具有实际上的性能优势。

在Python中, NumPy 数组是最常用的多维数组的实现。JavaScript具有TypedArrays,其仅含有一个单一的数值类型,但是是一维的,因此需要在其之上构建多维索引。

由于实际上这些数组可能会非常大,我们不想在语言运行时间拷贝它们。那不仅仅会花相当长的时间,而且在内存中同时保留两个拷贝将会加重浏览器所具有的被限制的内存的负担。

幸运的是,我们可以不用拷贝来共享数据。多维数组通常是用少量用于描述值类型和数组形状及内存分布的元数据来实现的。数据本身是从元数据中通过指针访问的另一个内存区域。该内存处于一个叫作“WebAssembly堆”的区域,这带来一个优势,因为其可以从JavaScript和Python中同时访问。我们可以简单地在语言之间拷贝元数据(其本身非常小),并保持指针指向WebAssembly堆中的数据。

这个想法目前对一维数组进行了实现,同时对于高维数组目前工作还处于次最优状态。我们需要对JavaScript端不断改善来获得一个可工作的有用的对象。迄今为止还没有一个用于JavaScript多维数组的很显然的选择。有前景的项目,比如Apache Arrow](https://arrow.apache.org/) 和xnd’s ndarray就正是致力于解决该问题,旨在使语言间运行时的内存结构化数据的传递更容易。对这些项目的构建的调查仍在进行当中以使得这类型的数据转换更为强大。

实时交互可视化

在浏览器中进行数据科学计算相比于如Jupyter一样在远程内核中进行计算的一大优势就是,交互式可视化不用通过网络来传输数据并重新处理和展示这些数据。这很大程度地减少了延迟—用户移动鼠标的时刻与屏幕更新并显示图案的时刻之间的间隔时间。

要使得其能工作需要上面描述到的所有的技术片段能够很好地协同工作。我们使用matplotlib来看一下用于展示正态分布如何工作的交互性示例。首先,通过Python的Numpy产生随机数据。接下来,Matplotlib接管该数据,并使用内建的软件渲染器来将其绘出。它使用Pyodide对零拷贝共享数组的支持来将像素回馈给JavaScript端,在这里数据最终被渲染为HTML的画布。然后浏览器接管工作,将像素显示到屏幕上。用来支持交互性操作的鼠标和键盘事件通过从Web浏览器到Python的回调函数的调用来处理。

软件包

Python科学计算栈并不是单独的一个庞然大物—它实际上是一个一系列相互之间松关联的包在一起工作构造的环境。其中最流行的是用于数值数组和基本计算的 NumPy,用于更复杂和通用计算如线性代数的 Scipy ,用于数据可视化的Matplotlib,用于表格数据或者数据帧的的Pandas 。你可以在 这里看到Pyodide为浏览器构建的完整的持续更新的包列表。

其中一些包是很容易直接被引入Pyodide的。通常来说,任何用纯Python写而没有使用编译语言扩展的部分都是很容易。而稍微有点儿困难的类型是像Matplotlib这样的库,其需要特定的代码来在HTML画布中展示绘图。属于极其困难的一端是Scipy库,它一直是一个相当大的挑战。

Roman Yurchak致力于使Scipy中大量的古老的Fortran被编译到WebAssembly。Kirill Smelkov改善了emscripten以使得共享对象能够被其它共享的对象使用,将Scipy控制到一个可管理的大小。(这些外部贡献者的工作由 Nexedi提供支持)。如果你在将一个包转为Pyodide的时候很费劲儿,可以在GitHub上联系我们: 很有可能我们已经之前就遇到过你的问题。

由于我们不能提前预测用户最终需要用哪些库,它们被按需单独地下载到浏览器中。例如,当你导入NumPy时:

Pyodide抓取NumPy库(以及其所有的依赖)并将它们载入到浏览器。再一次,这些文件只需要被下载一次,然后就被存储在浏览器缓存中。

将新的包添加到Pyodide目前是一个半手动的过程,涉及到添加文件到Pyodide构建中。我们长期以来更希望采用分发的方式来实现该过程,为了任何人都可以在不用遍历单个项目的情况下能贡献包到生态中。最棒的例子就是conda-forge。能将它们的工具扩展来支持WebAssembly作为一个平台将会非常棒,而不是花费大力气重新打造一个。

此外,Pyodide将会很快支持 直接从PyPI(Python的主要社区包仓库)中加载包,如果该包是纯Python编写而且以wheel格式发布。

最后

Python崛起并且风靡,因为优点多、应用领域广、被大牛们认可。学习 Python 门槛很低,但它的晋级路线很多,通过它你能进入机器学习、数据挖掘、大数据,CS等更加高级的领域。Python可以做网络应用,可以做科学计算,数据分析,可以做网络爬虫,可以做机器学习、自然语言处理、可以写游戏、可以做桌面应用…Python可以做的很多,你需要学好基础,再选择明确的方向。这里给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

👉Python所有方向的学习路线👈

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

👉Python必备开发工具👈

工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。

👉Python全套学习视频👈

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

👉实战案例👈

学python就与学数学一样,是不能只看书不做题的,直接看步骤和答案会让人误以为自己全都掌握了,但是碰到生题的时候还是会一筹莫展。

因此在学习python的过程中一定要记得多动手写代码,教程只需要看一两遍即可。

👉大厂面试真题👈

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
461e47e58e503d2bc1dc6f4668534a.png)

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值