从 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