PyScript介绍:在浏览器中运行Python的方法

1,004 阅读21分钟

长期以来,JavaScript一直是前端开发的主导语言,因为它能够在浏览器中原生运行,并通过DOM API与HTML和CSS交互。随着WebAssembly的出现,情况开始慢慢改变。Go、Rust、C、C++等语言现在可以以接近原生的速度在浏览器中运行,而Python也没有被落下。

随着PyScript的引入,前端开发者现在可以用Python构建丰富的前端。此外,他们还可以利用Python生态系统,其中有一些有用的科学模块,如NumPyMatplotlib和其他许多模块。

前提条件

要获得本教程的大部分内容,你将需要

  • 对HTML、CSS和JavaScript有基本了解
  • 熟悉Python语法
  • 一个网络服务器。我们将使用Python创建一个简单的服务器,所以请确保你的系统中安装了Python。
  • 一个网络浏览器;PyScript 文档目前推荐使用 Chrome 浏览器。

什么是PyScript?

PyScript 是一个开源的网络框架,允许你使用 Python 创建前端网络应用。通过 PyScript,您可以在 HTML 中嵌入 Python 代码,或者链接到一个 Python 文件,代码将在浏览器中执行 - 而无需在后端运行 Python。

PyScript由Anaconda创建,于4月30日在2022年美国PyCon大会上公开发布。在撰写本文时,PyScript处于alpha状态,正在积极开发中,因此,由于它还没有稳定地发布,所以可以预期会有一些突破性的变化和更新的功能。

PyScript是如何工作的?

PyScript建立在Pyodide之上,它将CPython移植到WebAssembly。WebAssembly是一种低级别的二进制格式,允许你用其他语言编写程序,然后在浏览器中执行。有了WebAssembly中的CPython,我们可以在浏览器中安装和运行Python包,而PyScript则抽象了大部分Pyodide操作,让你专注于在浏览器中用Python构建前端应用程序。

为PyScript设置你的项目文件夹

在我们开始使用PyScript之前,让我们创建一个存放代码的目录。

要做到这一点,打开你的终端,使用mkdir 命令在你选择的位置创建项目目录。

mkdir pyscript_demo

接下来,使用cd 命令移动到你刚刚创建的目录中。

cd pyscript_demo

禁用自动格式化工具,如Prettier

通常,前端开发人员在他们的文本编辑器中使用自动格式化工具(如Prettier)来在保存时格式化代码。虽然这对HTML、CSS和JavaScript很有效,但在Python代码中会引起问题,因为Python对缩进很严格。

目前,像Prettier这样的自动格式化工具不能识别PyScript的语法,在写这篇文章时,PyScript才刚刚诞生两个月。这些工具将Python代码自动格式化为JavaScript,这就破坏了代码的缩进。为了补救这个问题,我们将暂时禁用这个目录的自动格式化功能。

假设你使用的是VSCode,我们可以按以下方法禁用自动格式化。

在你的项目目录中,创建一个.vscode 目录,用以下命令导航到该目录。

mkdir .vscode && cd .vscode

接下来,创建一个settings.json 文件并添加以下内容。

{
  "editor.formatOnSave": false
}

这样一来,这个目录的保存时自动格式化功能在VSCode中已经被禁用,我们现在可以开始使用PyScript了。

开始使用

现在我们的目录已经为PyScript设置好了,我们将首先在HTML页面的<head> 部分添加PyScript资产的链接,其中包括一个CSS文件和JavaScript文件。

一旦添加了资产,你就可以通过两种方式在HTML文件中使用PyScript:

  • 内部PyScript。你可以在HTML文件的<py-script> 标签中编写并放置你的Python代码。<py-script> 标签可以被添加到<head><body> 标签中,这取决于你手头的任务。
  • 外部PyScript。这是在一个以.py 为结尾的文件中编写Python代码,然后可以在<py-script> 标签中使用src 属性来引用该文件。

内部PyScript

开始使用PyScript的最简单、最快的方法是将Python代码嵌入到HTML文件中。让我们这样做吧!

打开你喜欢的文本编辑器,创建hello-world.html 文件并添加以下内容。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Hello World!</title>
    <!-- linking to PyScript assets -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  <!-- Put Python code inside the the <py-script> tag -->
    <py-script>print("Hello World!")</py-script>
  </body>
</html>

<head> 部分,我们链接到pyscript.css 文件,其中包含 PyScript 视觉组件、REPL、PyScript 载入器等的样式。之后,我们链接到pyscript.js 文件,该文件设置了使用PyScript的必要功能,比如创建像<py-script> 这样的标签,你可以在那里写你的Python代码。

<body> 标签中,你将Python代码嵌入到<py-script> 标签中。我们现在要保持简单,所以我们只是向用户打印Hello World

确保将你的文件保存在项目目录的根部,并在Chrome浏览器中打开hello-world.html 。它将需要几秒钟的时间来加载,一旦页面加载完毕,它将看起来类似于这样:

Browser prints "Hello, World!"

外部PyScript

虽然把Python代码放在<py-script> 标签中也可以,但一个更好、更可扩展的方法是把代码添加到一个外部文件中,当你创建更多的HTML页面或你的脚本变大时,在HTML文件中引用它。

以下是你应该考虑在外部文件中使用PyScript代码的一些原因。

  • 该文件可以被浏览器缓存,从而带来更快的性能
  • 你可以在多个页面中引用该文件,减少重复。
  • 你的Python代码可以用black或Python linters等工具进行格式化。这些工具目前对嵌入 HTML 文件的 Python 代码不起作用。

为了在外部使用 PyScript,我们将创建一个index.html 文件,一个以.py 结尾的 Python 文件,其中包含我们的 Python 代码,最后在index.html 文件中引用该 Python 文件。

创建index.html 文件

创建一个index.html 文件并链接到 PyScript 资产。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Greetings!</title>
    <!-- linking to PyScript assets -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  </body>
</html>

这个文件并没有做什么;我们只是链接到PyScript资源。为了使它更有用,我们将创建一个main.py 文件,我们的Python代码将存放在这里。

创建main.py 文件

让我们创建一个Python函数,打印一条问候信息。

在你的文本编辑器中,创建main.py 文件并添加下面的代码。

def greetings(name):
    print(f'Hi, {name}')

greetings('John Doe')

greetings() 函数接收一个name 参数,并打印一个带有存储在name 参数中的名称的问候信息。当我们用John Doe 作为参数调用greetings() 函数时,它打印出hi, John Doe

在HTML文件中链接main.py 文件

现在你已经创建了 Python 代码,你将在index.html 文件中引用main.py 文件。

打开index.html 并在<body> 标签内添加这一行。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Greetings!</title>
   <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  // add the following line
  <py-script src="./main.py"></py-script>
  </body>
</html>

<py-script> 标签有一个src 标签,它接受 Python 文件的文件路径。

在浏览器中打开index.html 文件

现在一切准备就绪,我们将在浏览器中打开index.html

然而,由于跨源资源共享(CORS)策略错误,浏览器将拒绝加载和执行外部Python文件。为了解决这个问题,我们将需要使用一个服务器。好在 Python 带来了一个我们可以使用的 web 服务器!这个服务器不需要由 Python 创建,你可以使用live-server或你选择的任何服务器。

要创建一个服务器,在你项目的根目录下打开终端,运行下面的命令。

python -m http.server

接下来,打开Chrome浏览器并访问 [http://0.0.0.0:8000/](http://0.0.0.0:8000/).服务器将自动加载index.html 文件,你将看到以下内容。

The browser prints our greeting

在本教程的其余部分,我们将引用一个外部 Python 文件,这需要我们使用一个服务器来避免 CORS 错误,有时为了简洁起见,我们会在 HTML 中嵌入 Python 代码。

使用PyScript REPL

PyScript 自带了一个读-评-印循环 (REPL),你可以用它来实验和尝试 Python 代码。

要使用 REPL,在你的index.html 文件中的<body> 标签中添加<py-repl> 标签。

<!DOCTYPE html>
  ...
  <body>
  <py-script src="./main.py"></py-script>
  // add the following tag
  <py-repl></py-repl>
  </body>
</html>

在服务器仍然运行的情况下,访问 [http://0.0.0.0:8000/](http://0.0.0.0:8000/).你会看到一个新的部分,你可以在那里输入Python代码。

你可以导入模块,评估表达式,创建函数,以及做更多的事情。要看一个表达式的评估结果,你需要点击绿色的播放图标。

下图显示了你可以做的一些操作:

The PyScript REPL in the browser

现在我们已经知道如何使用 REPL,接下来我们将学习如何在 PyScript 中创建和使用模块。

在 PyScript 中使用 Python 模块

在本节中,我们将创建一个自定义的 Python 模块并在我们的代码中使用它。我们还将使用来自 Python 标准库的模块,以及第三方模块。

为了使用模块,我们将引入一个新标签,<py-env> ,它允许我们引用模块或模块文件路径。

创建自定义模块

让我们创建一个包含两个函数的本地模块。

在你的项目目录下创建一个mathslib.py 文件,并添加以下代码。

def add(num1, num2):
    return num1 + num2

def subtract(num1, num2):
    return num1 - num2

这里我们创建了两个函数,做加法和减法运算。

接下来,创建一个modules.html 文件并添加以下内容。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>local modules</title>
    <!-- linking to PyScript assets -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
 <py-env>
    - paths:
        - mathslib.py
  </py-env> 
  <py-script>
from mathslib import subtract
print(subtract(8, 4))
  <py-script>
  </body>
</html>

<body> 标签中,我们使用<py-env> 标签,它接受一个以paths 为键的YAML列表。mathslib.py 是相对于modules.html 文件的自定义模块的文件路径。一旦指定了自定义模块的路径,PyScript将导入文件中的模块。

装入模块后,在<py-script> 标签中,我们从mathslib.py 导入subtract() 函数,并用参数84 调用该函数。

在服务器运行的情况下,访问 [http://0.0.0.0:8000/modules.html](http://0.0.0.0:8000/modules.html)你会看到一个与此类似的页面。

The result of our subtraction function

从 Python 标准库中导入模块

PyScript,在Pyodide的帮助下,提供了对Python标准库中大量可用模块的访问,这些模块可以随时使用,但以下模块除外。

  • tkinter
  • venv
  • dbm

请访问Pyodide文档以查看一个全面的列表。另外,注意那些已经包含但没有功能的模块,如多处理、线程和套接字模块。

标准库中的模块在PyScript命名空间中是默认可用的;你只需要导入它们就可以在文件中使用它们。

还是在modules.html 文件中,修改<py-script> 标签中的 Python 代码,使用random 模块生成一个随机数。

from mathslib import subtract
import random
print(subtract(8, 4))
print("random number generated: ")
print(random.randint(1, 12))

现在访问 [http://0.0.0.0:8000/modules.html](http://0.0.0.0:8000/modules.html)页面,你会看到每次刷新页面时都会生成一个随机数。

The result of our subtraction function and our randomly generated number

使用第三方软件包

除了使用内置的Python模块,你还可以使用Pyodide中运来的第三方库,比如:

关于支持的第三方软件包的完整列表,请访问Pyodide文档或密切关注PyodideGitHub repo

要添加一个第三方软件包,请创建一个新的HTML文件,third-party.html ,并添加以下代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>local modules</title>
    <!-- linking to PyScript assets -->
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  <!-- thirdparty dependencies added here -->
 <py-env>
    - numpy 
    - matplotlib
  </py-env> 
  <py-script>
import numpy as np
import matplotlib.pyplot as plt
arr = np.array([1, 2, 3, 4, 5])
plt.plot(arr)
plt
  <py-script>
  </body>
</html>

<py-env> 标签中,我们添加一个我们想在项目中使用的第三方包的列表,这些包是NumPy和Matplotlib包。接下来,在<py-script> 标签中,我们将NumPy导入为np ,Matplotlib导入为plt 。之后,我们调用NumPy的array 方法,该方法会创建一个数组,然后存储在arr 变量中。之后,我们以数组arr 为参数,调用Matplotlib的plot 方法来绘制图形。

确保你的文件被保存,然后访问 [http://0.0.0.0:8000/third-party.html](http://0.0.0.0:8000/third-party.html)页面。你应该看到一个类似于以下的图表。

Our example line graph

现在你已经了解了如何使用自定义的、内置的模块和第三方软件包,我们将在下一节中学习如何访问和操作HTML元素。

使用PyScript访问和操作HTML元素

在本节中,我们将学习如何使用ID或CSS类来选择一个HTML元素,修改一个元素,将事件附加到一个元素上,并使用PyScript创建新的元素。

使用Element

PyScript提供了Element 类,它允许你使用其ID来选择一个HTML元素。

为了了解它是如何工作的,请创建一个elements.html 文件并插入以下内容:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Element class</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  <ul id="navigation">
    <li class="home">home</li>
    <li class="about">about</li>
    <li class="services">services</li>
    <li class="contact">contact</li></ul>
  </div>
  <div id="output"></div>
  <py-script src="./access-elements.py"></py-script>
  </body>
</html>

<body> 标签中,我们有一个ID为navigation<ul> 元素。我们将使用ID,用Element 类来选择这个元素。被选中的实例将给我们提供一些方法,我们可以用这些方法来选择后代并对其进行操作。

我们将使用的另一个标签是ID为output<div> 。我们将修改其innerHTML ,以写入一个新值。最后,在<div> 标签之后,我们链接到access-elements.py 文件,该文件将包含我们的 Python 代码。它还不存在,所以让我们继续创建它。

一旦你创建了access-elements.py 文件,就把下面的代码添加到它里面:

ul_element = Element("navigation")
first_element = ul_element.select('.home').add_class('first')
second_element = ul_element.select('.about').remove_class('about')
div_element = Element("output")
div_element.write("Value set from PyScript")

在前面的代码中,我们使用Element 类来访问使用navigation ID 的<ul> 元素。

当使用Element 类选择一个元素时,你可以利用以下一些方法:

  • write():设置innerHTML 的值
  • select():使用CSS选择器来寻找下级元素
  • add_class():为一个元素添加一个或多个类
  • remove_class():从一个元素中删除一个或多个类

在第二行,我们使用select() 方法来选择<ul> 元素的第一个子元素,使用它的类名home 。选择完子元素后,我们调用add_class() 方法为<li> 元素添加一个新的类first

在第三行,我们通过它的类名about ,访问第二个子元素,然后使用remove_class() 方法删除它的类about

接下来,我们调用ID为outputElement 类,它提供了对<div> 元素的引用,该元素位于elements.html 文件中的ul 元素之后。最后,我们用字符串Value set from PyScript 调用write() 方法。该方法将把<div> 元素innerHTML 的值设置为字符串参数。

在服务器仍在运行的情况下,访问 [http://0.0.0.0:8000/elements.html](http://0.0.0.0:8000/elements.html)并检查<ul> 元素。你会看到第一个<li> 元素现在有一个额外的类 (first),第二个元素没有类,而div 元素现在有我们在 Python 中设置的文本。

Our <ul> element when inspected

将事件附加到元素上

我们现在可以选择HTML元素并做一些基本的操作。在这一节中,我们将给元素附加一个点击事件,并在元素被点击时让 Python 代码执行。

创建一个events.html 文件,并编写下面的代码。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Adding Events</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  <button id="click-btn" class="bg-blue-500 text-white" pys-onClick="handle_click">Click</button>
  <div id="output"></div>
  <py-script src="./event-handlers.py"></py-script>
  </body>
</html>

<body> 标签中,我们有一个<button> ,其中有一个class 属性,包含一些属于pyscript.css 文件的类。<button> 标签也有一个pys-onclick 属性,它为按钮附加了一个click 事件。pys-onclick 属性接受函数名称handle_click ,这将是按钮被点击时运行的函数。

接下来,我们有一个ID为outputdiv 元素。我们将用我们定义的handle_click 函数修改innerHTML 中的元素。

最后,我们链接到event-handlers.py 文件,该文件将包含事件处理函数。

让我们定义event-handlers.py ,并添加以下内容:

def handle_click(e):
    pyscript.write("output", "you clicked the button")

handle_click 函数有一个参数,e ,这是当你点击按钮时自动传递给函数的一个事件对象。在函数内部,我们调用PyScript的write() 方法,它需要两个参数:元素IDoutput 和我们要写的值,在我们的例子中是you clicked the button

请确保你的服务器正在运行。

python -m http.server

然后访问URL [http://0.0.0.0:8000/events.html](http://0.0.0.0:8000/events.html)在Chrome中。当页面加载时,点击按钮,会出现一条 "你点击了按钮 "的信息。

The output after we click the button

使用JavaScript来访问和操作DOM

PyScript提供了一个js 模块,让你可以访问JavaScript方法,如querySelector(),createElement(),appendChild() 等,以访问和操作HTML元素。有了这些方法,你就可以把 JavaScript 和 Python 混在一起,做一些很酷的 DOM 操作。下面是一个例子。

import js

print(js.window.innerHeight)

nav = js.document.createElement("div")
js.document.body.prepend(nav)

js.console.log("nav element created")

正如你所看到的,我们将Python代码方法(如print() )与JavaScriptwindowdocument 属性混合在一起。

在这一节中,我们将主要关注document 方法,很方便地,PyScript使它在Python范围内自动可用。我们甚至不需要导入js 模块来使用document 方法。

创建一个dom.html 文件并添加以下代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Mixing JavaScript and Python</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  <ul id="navigation">
  </ul>
  <py-script src="./js-dom.py"></py-script>
  </body>
</html>

<body> 标签中,我们只有一个空的<ul> 元素,其 ID 为navigation 。接下来,我们引用js-dom.py ,它将包含我们的 Python 代码。

创建js-dom.py 文件并添加以下内容。

nav_parent = document.querySelector('#navigation')
nav_texts = ["home", "about", "services", "contact"]
for text in nav_texts:
    nav_item = document.createElement("li")
    nav_item.textContent = text
    nav_item.className = "nav_element"
    nav_parent.appendChild(nav_item)

在第一行,我们调用document 模块的querySelector() 方法,参数为#navigation 。该方法将找到并返回一个 ID 为navigation 的元素,这就是dom.html 文件中的<ul> 元素。

在第二行,我们创建一个导航文本的列表,并将其存储在nav_texts 变量中。之后,我们对nav_texts 列表进行迭代。在每次迭代中,我们用一个字符串li 来调用createElement() 方法,创建一个<li> 元素。

之后,我们使用textContent 属性向<li> 元素添加文本,并使用className 属性向<li> 元素添加一个类名nav_element 。最后,我们通过调用appendChild() ,以nav_item 作为参数,将<li> 元素附加到<ul> 元素上。

确保你的文件已被保存,服务器仍在运行。访问 [http://0.0.0.0:8000/dom.html](http://0.0.0.0:8000/dom.html),你会看到一个类似于以下的页面。

A list of navigation texts

如果你进一步研究并检查这些元素,你会看到<li> 元素已经用类名nav_element 创建,这是我们在 Python 中设置的。

List items with the nav_element class

现在我们可以使用Element 类访问和操作 DOM,将事件附加到元素上,并使用 JavaScript 来查询和修改 DOM。接下来,我们将使用PyScript从一个API中获取数据。

从API中获取和渲染数据

在这一节中,我们将使用PyScript向API发送一个GET 请求来获取数据。我们将使用的API是随机数据API。我们将创建一个带有点击事件的按钮,在每次点击按钮时运行一个调用API的函数。

在你的目录中创建一个fetch_data.html 文件,并添加以下内容。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Fetch data from API</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  <button id="get-name" class="bg-green-600 text-white" pys-onClick="get_random_name">Generate Random Name</button>
  <div id="output"></div>
  <py-script src="./fetch.py"></py-script>
  </body>
</html>

这时的代码应该是熟悉的。最重要的部分是<button> 标签,它有接受get_random_name() 函数的pys-onClick 属性。该函数将驻留在底部链接的fetch.py 文件中。让我们继续创建该文件。

在你的文本编辑器中,创建一个名为fetch.py 的新文件,内容如下。

from pyodide.http import pyfetch
import asyncio

async def get_random_name(e): 
    response = await pyfetch(url="https://random-data-api.com/api/name/random_name", method="GET")
    data = await response.json()
    first_name = data.get('first_name')
    middle_name = data.get('middle_name')
    last_name = data.get('last_name') 
    output =  f"Random name: {first_name} {middle_name} {last_name}"
    pyscript.write('output', output)

在第一行,我们从pyodide.http 模块中导入pyfetch() 方法,它允许我们进行异步的网络请求。在第二行,我们导入asyncio 模块,它是 Python 标准库的一部分,提供了asyncawait 关键字,这些关键字对于创建异步函数很有用。

接下来,我们定义一个异步函数get_random_name() ,用asyncio 模块中的async 关键字作为前缀。在这个函数中,我们调用了接受两个参数的pyfetch() 方法。

  • URL:API端点
  • method:Species 你想使用的HTTP方法,这里是GET 方法。

pyfetch() 运行时,它返回一个对象,然后将其存储在response 变量中。在接下来的一行中,我们在response 对象上调用json() 来解析 JSON 并返回一个 Python 字典,然后将其存储在data 变量中。

在接下来的几行中,我们从data dict 中提取名字、中间名和姓氏,并将它们存储在各自的变量中。output最后,我们使用Python的f-strings将名字连接起来,并调用pyscript.write() 方法,将数据写入ID为<div> 的元素中。

确保你的服务器正在运行,并访问 [http://0.0.0.0:8000/fetch_data.html](http://0.0.0.0:8000/fetch_data.html)页面。一旦页面加载,点击Generate Random Name 按钮。你会看到,每次点击该按钮都会产生一个新的名称。

The random name from the Random Name API

持续性地使用数据localStorage

在本节中,我们将使用本地存储来保存和检索数据。本地存储是网络浏览器中的一个对象,它可以存储没有过期日期的数据。Python 可以通过从js 模块中导入本地存储来使用它。

为了使用本地存储,我们将创建一个文本区,允许用户输入评论。如果他们想保存评论,他们将点击一个save 按钮,该按钮将运行一个函数,将数据保存在本地存储中。每次访问该页面时,数据将被从本地存储中检索出来,文本区将被设置为该数据。

创建一个storage.html 文件并添加以下内容。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Store data in local storage</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  <textarea id="comment" class="block border"></textarea>
  <button id="save" class="bg-green-600 text-white"
   pys-onClick="save_comment">Save</button>
  <py-script src="./local-storage.py"></py-script>
  </body>
</html>

<body> 标签中,我们创建一个ID为comment<textarea> 标签。我们将使用这个ID来获取Python中的文本区元素的引用。接下来,我们有一个按钮,它的ID是save ,还有一个点击事件,它将调用函数save_comment ,我们还没有定义。最后,我们引用local-storage.py ,它将包含我们的 Python 代码。现在我们来创建这个文件。

创建local-storage.py 并添加以下内容:

from js import localStorage

def save_comment(e):
    text =  Element("comment").value
    localStorage.setItem("comment", text)

if localStorage.getItem("comment"):
    text_area =  Element("comment")
    text_area.write(localStorage.getItem("comment"))

首先,我们从js 模块中导入localStorage 对象。接下来,我们定义save_comment() 函数,它以e 为参数。在该函数中,我们调用ID为commentElement 类,以获得一个文本区域的引用。一旦该方法找到了文本区域,我们使用value 属性来获得文本区域的内容,并将该值存储在text 变量中。在下一行,我们调用localStorage 对象的setItem() 方法,将注释文本保存在localStorage 对象的comment 键下。

现在,save_comment() 函数只有在点击save 按钮时才会运行。然而,在save_comment() 函数之外进行,该函数后面的行将只在页面加载时执行。

当页面第一次加载时,我们使用if 语句来检查localStorage 对象在comment 键下是否有数据。如果是真的,我们使用Element 类来引用文本区,并将其实例存储在text_area 变量中。接下来,我们调用text_area 实例的write() 方法,用本地存储的数据更新文本区的内容。

确保你的服务器正在运行并访问 [http://0.0.0.0:8000/storage.html](http://0.0.0.0:8000/storage.html).输入任何你喜欢的文本,然后点击保存按钮。

A text area containing text

接下来,刷新URL,你会看到文本区包含你在初次访问时保存的文本。

Our text area, this time populated by text from localStorage

就这样,你现在知道了如何使用PyScript来利用localStorage 。接下来,我们将使用PyScript读取文件系统中的一个文件。

与文件系统互动

在本节中,我们将使用PyScript从本地文件系统中的明文文件中读取数据,并将其内容追加到DOM中。

首先,让我们创建一个包含我们想要读取的数据的文件。在你的主项目目录下,运行下面的命令来创建并移动到一个新的目录。

mkdir data &amp;&amp; cd data

接下来,创建一个names.txt 文件并添加以下内容,这些内容是Python网络框架的名称。

Django
Flask
FastAPI
web2p

保存该文件并回到你的项目根目录。

cd ..

创建好文件后,在你的文本编辑器中创建一个file-system.html 文件,内容如下。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Read data from file system</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
   <py-env>
    - paths:
        - /data/names.txt
  </py-env>
  <ul id="frameworks">
  </ul>
  <py-script src="./read-file.py"></py-script>
  </body>
</html>

<py-env> 标签中,我们指定到names.txt 的路径,它是相对于file-system.html 的路径。接下来,我们创建一个空的<ul> 标签,有一个frameworks ID。最后,我们引用read-file.py ,我们将很快定义这个标签。

创建一个具有以下内容的read-file.py

ul_element = document.querySelector("#frameworks")
with open("names.txt") as f:
    for line in f:
        li_element = document.createElement("li")
        li_element.innerText = line
        ul_element.appendChild(li_element)

在第一行,我们用一个ID选择器#frameworks ,调用querySelector() 方法,得到一个对<ul> 元素的引用。在第二行中,我们用文件名names.txt 来调用open() 方法,并将文件对象存储为f

with 语句中,我们对存储在文件对象f 中的每一行进行迭代。在每个迭代过程中,我们使用document 对象的createElement() 方法创建一个<li> 元素。接下来,我们使用li_element 实例的innerText 属性,将<li> 的文本内容设置为line 变量中的值。最后,我们通过调用以li_element 为参数的appendChild() ,将<li> 元素附加到<ul> 元素上。

再次启动服务器(如果你之前停止了它)。

python -m http.server

访问 [http://0.0.0.0:8000/file-system.html](http://0.0.0.0:8000/file-system.html)URL,你会看到纯文本文件的内容显示在页面上。

A list of names read from the plaintext file

如果你检查这些元素,你会看到有四个<li> 元素被附加到<ul> 元素上。

A list of names inspected in the console

有了这个,你现在可以读取文件系统中的文件了。你可以用同样的方法来读取CSV文件和其他许多文件格式。

总结

在本教程中,我们学习了如何使用 PyScript REPL,创建自定义模块,使用 Python 标准库中的模块,以及导入第三方模块。我们还学习了如何使用 PyScript 访问和操作元素,提出 API 请求,使用localStorage ,以及从文件系统中读取纯文本文件。