发布于 2026-01-06 10 阅读
0

从 JavaScript 向 Python 进行对话(以及反向对话!)DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

从 JavaScript 向 Python 调用(以及反向调用!)

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

很多初学者都会遇到一个难题:如何在不同的编程语言之间传递数据。理解起来其实很简单,一个变量中存储着一个值,可以把它从一个函数传递到另一个函数。然而,要想在程序内部实现更多功能,我们必须以某种方式序列化数据。我们将探讨这两种语言之间通信的两种方式:通过新的 Fetch API 发送 AJAX 请求,以及在本地进程之间进行管道传输。

数据序列化是指将值、对象或数据结构转换为可存储或传输的格式。更重要的是,它需要在另一端被重新组合。我们来看一下 JavaScript 对象表示法 (JSON)。JSON 是一种人类可读的格式,机器也能够轻松地读取和写入。它的规范非常简洁,一杯咖啡的时间就能看完。JavaScript 和 Python 都提供了用于解析和写入 JSON 的标准库方法。

JSON 速成课程

JSON 基于两种数据结构:对象(键/值对,类似于 JavaScriptObject和 PythonObject中的字符串Dictionary)和数组(一系列数据,类似于 JavaScript和 Python 中的数组) 。ArrayList

/* JavaScript
   Try this out in your developer console! */

var person = {"name":"Andrew", "loves":"Open Source"};
var asJSON = JSON.stringify(person);

// `person` is of type 'object'
console.log(`person is of type ${typeof person}`);

// `asJSON` is of type 'string'
console.log(`asJSON is of type ${typeof asJSON}`);

// We can convert it back to an object by parsing it
// `asObject` is of type 'object'
var asObject = JSON.parse(asJSON);
console.log(`asObject is of type ${typeof asObject}`);

让我们使用 Python 的标准库模块来实现同样的功能json

# python

animal = {'type':'cat', 'age':12}
as_json = json.dumps(animal)

print(type(animal))  # prints '<class 'dict'>'
print(type(as_json))  # prints '<class 'str'>'

# now back again
as_object = json.loads(as_json)
print(type(as_object))  # prints '<class 'dict'>'

总结:在 JavaScript 中,使用 `json.json` 将数据序列化为 JSON,JSON.stringify()并使用 `json.json` 解析数据JSON.parse()。这在浏览器和 Node.js 中都适用。在 Python 中,首先导入 `json.json`json模块,然后使用 `json.json` 进行序列化json.dumps(),并使用 `json.json` 进行解析json.loads()

通过 AJAX 进行通信

过去,我们会使用XMLHttpRequest来实现这个功能,但相对较新的Fetch API使用起来要方便得多。首先,我们将编写一个简单的 Python Web 服务器,然后我们将探讨如何与浏览器之间来回传递 JSON 数据。

Flask是一个“微框架”。它不仅用起来非常有趣,而且也非常适合用来做原型设计。我们将使用它的jsonify模块,该模块可以写入/解析 JSON,并设置正确的响应头(application/json MIME 类型)。安装和运行调试服务器需要两条操作系统特定的命令。就我个人而言,在 OS X 系统上,这两条命令分别是 `flask --version` 和 `flask pip install flask--version` FLASK_APP=app.py flask run

跟着代码编写,或者 fork此仓库以获取所有示例的代码。

# app.py
from flask import Flask, jsonify, request, render_template
app = Flask(__name__)

@app.route('/hello', methods=['GET', 'POST'])
def hello():

    # POST request
    if request.method == 'POST':
        print('Incoming..')
        print(request.get_json())  # parse as JSON
        return 'OK', 200

    # GET request
    else:
        message = {'greeting':'Hello from Flask!'}
        return jsonify(message)  # serialize and use JSON headers

@app.route('/test')
def test_page():
    # look inside `templates` and serve `index.html`
    return render_template('index.html')

服务器运行正常,并能提供可运行 JavaScript 的页面后,我们就可以开始使用 JSON 了!我们将使用 Fetch API 发送一个 GET 请求,并接收来自 Flask 的响应。在编写任何客户端代码之前,我总是使用Postman来测试我的 Web 服务器——它是免费的,也是 API 测试和开发领域的行业标准工具之一。

我在<script>`<div>` 标签内运行以下代码片段templates/index.html。标签内没有其他内容index.html,所以渲染后的页面是空白的。所有操作都在控制台中执行。

// GET is the default method, so we don't need to set it
fetch('/hello')
    .then(function (response) {
        return response.text();
    }).then(function (text) {
        console.log('GET response text:');
        console.log(text); // Print the greeting as text
    });

// Send the same request
fetch('/hello')
    .then(function (response) {
        return response.json(); // But parse it as JSON this time
    })
    .then(function (json) {
        console.log('GET response as JSON:');
        console.log(json); // Here’s our JSON object
    })

太棒了!我们已经实现了 Python 与客户端 JavaScript 使用 JSON 进行数据序列化。现在让我们反过来,从浏览器向 Python 发送 JSON 数据。我们将再次使用 Fetch API,但这次是 POST 请求而不是 GET 请求。

新手小贴士:记住“寄信”和“取信”的区别。寄信时,你带着写满信息的信件去邮局。取信时,你再次去邮局,但这次是去领取别人留给你的邮件。

// POST
fetch('/hello', {

    // Specify the method
    method: 'POST',

    // JSON
    headers: {
        'Content-Type': 'application/json'
    },

    // A JSON payload
    body: JSON.stringify({
        "greeting": "Hello from the browser!"
    })
}).then(function (response) { // At this point, Flask has printed our JSON
    return response.text();
}).then(function (text) {

    console.log('POST response: ');

    // Should be 'OK' if everything was successful
    console.log(text);
});

有了这两个核心构建模块,我们就实现了通过 HTTP 进行 JSON 通信。但是请注意,您应该catch在这些 Promise 的末尾添加 catch 语句。我只是为了清晰起见才省略了这些语句。最好优雅地处理错误,这样我们就可以告知用户连接已断开或我们这边出现了错误。有关 catch 语句的文档请参见此处

如果你用 Python 与 Node.js Web 服务器通信,你可能会用到requests模块,它的语法与 Fetch API 几乎完全相同。

通过流程进行交流

我们将双向创建进程,以便观察 Node.js 和 Python 之间的通信情况。在两种情况下,我们都会监听子进程的标准输出流。假设我们有一个程序,它会不定期地报告数据,例如温度传感器。我们希望监听该程序并存储它报告的值。

这是用 Python 编写的模拟传感器程序。它会将数据打印到标准输出(stdout)。我们将在 Node.js 中捕获这些数据。

# sensor.py

import random, time
while True:
    time.sleep(random.random() * 5)  # wait 0 to 5 seconds
    temperature = (random.random() * 20) - 5  # -5 to 15
    print(temperature, flush=True, end='')

以这种方式通过管道传递信息时,务必刷新流,确保信息按预期到达标准输出(为什么 C 语言中使用 `fflush()` 函数?)。更多关于 Python 刷新流的信息请点击此处。此外,我们还要确保打印语句的末尾不包含任何额外信息(即使 `fflush()`parseFloat()函数会将其清除!),默认情况下,末尾会是换行符\n

我们现在使用的是 Node.js,想要获取当前报告的温度值。让我们创建sensor.py一个进程并监听标准输出事件,然后将数据通过管道传递给两个正在运行的语言。

// temperature-listener.js

const { spawn } = require('child_process');
const temperatures = []; // Store readings

const sensor = spawn('python', ['sensor.py']);
sensor.stdout.on('data', function(data) {

    // Coerce Buffer object to Float
    temperatures.push(parseFloat(data));

    // Log to debug
    console.log(temperatures);
});

翻转并反转

现在,让我们互换角色。这次我们用 Node.js 传感器和 Python 监听器!我们将尝试一种不同的方法,使用换行符 ( \n) 来分隔不同的读数,而不是等待事件触发。我们会将数据添加到缓冲区,直到遇到换行符。一旦遇到换行符,我们就收集到了完整的读数,可以将其存储起来。

首先,介绍Node.js中的等效传感器。

// sensor.js

function reportReading() {
    const temperature = (Math.random() * 20) - 5; // Range of -5 to 15
    process.stdout.write(temperature + '\n'); // Write with newline char
    setTimeout(reportReading, Math.random() * 5000); // Wait 0 to 5 seconds
}
reportReading();

现在用 Python 编写一个温度监听程序,该程序会将上述代码作为进程生成。

# temperature-listener.py

import sys
from subprocess import Popen, PIPE

temperatures = []  # store temperatures
sensor = Popen(['node', 'sensor.js'], stdout=PIPE)
buffer = b''
while True:

    # read sensor data one char at a time
    out = sensor.stdout.read(1)

    # after a full reading..
    if out == b'\n':
        temperatures.append(float(buffer))
        print(temperatures)
        buffer = b''
    else:
        buffer += out  # append to buffer

你可以运行node temperature-listener.js这两个命令python temperature-listener.py,结果都一样。随着新数据的到来,温度数组会不断增长,我们的调试日志行会输出以下信息。

[ 3.8075910850643098 ]
[ 3.8075910850643098, -1.5015912681923482 ]
[ 3.8075910850643098, -1.5015912681923482, 11.97817663641078 ]

我们已经了解了Python和JavaScript之间两种不同的通信方式,但如果您觉得这两种方式都不适合您,也别担心!在这两种语言之间传递数据的方法有很多,包括但不限于:命名管道、TCP套接字、WebSocket和文件轮询。


已有超过 150 人订阅了我的编程和个人成长简讯!

我在推特上发布关于科技的内容,账号是@healeycodes

文章来源:https://dev.to/healeycodes/talking-to-python-from-javascript-and-back-again-31jf