วิธีเรียกใช้ฟังก์ชัน Python จาก Node.js


208

ฉันมีแอปพลิเคชั่น Express Node.js แต่ฉันยังมีอัลกอริทึมการเรียนรู้ของเครื่องเพื่อใช้ใน Python มีวิธีที่ฉันสามารถเรียกใช้ฟังก์ชั่น Python จากแอพพลิเคชั่น Node.js ของฉันเพื่อใช้ประโยชน์จากพลังของไลบรารีการเรียนรู้ของเครื่องหรือไม่?


4
ม ไม่เคยใช้มันด้วยตัวเอง
univerio

22
สองปีต่อมาnode-pythonดูเหมือนว่าจะเป็นโครงการที่ถูกทอดทิ้ง
imrek


ดูเพิ่มเติมที่github.com/QQuick/Transcryptสำหรับการรวบรวมไพ ธ อนเป็น javascript แล้วเรียกใช้มัน
Jonathan

คำตอบ:


262

วิธีที่ง่ายที่สุดที่ฉันรู้คือใช้แพ็คเกจ "child_process" ซึ่งมาพร้อมกับโหนด

จากนั้นคุณสามารถทำสิ่งที่ชอบ:

const spawn = require("child_process").spawn;
const pythonProcess = spawn('python',["path/to/script.py", arg1, arg2, ...]);

แล้วสิ่งที่คุณต้องทำคือการทำให้แน่ใจว่าคุณimport sysในสคริปต์หลามของคุณแล้วคุณสามารถเข้าถึงarg1การใช้ sys.argv[1], arg2ใช้ sys.argv[2]และอื่น ๆ

ในการส่งข้อมูลกลับไปที่โหนดให้ทำดังต่อไปนี้ในสคริปต์ python:

print(dataToSendBack)
sys.stdout.flush()

จากนั้นโหนดสามารถฟังข้อมูลโดยใช้:

pythonProcess.stdout.on('data', (data) => {
    // Do something with the data returned from python script
});

เนื่องจากสิ่งนี้อนุญาตให้ส่งผ่านอาร์กิวเมนต์จำนวนมากไปยังสคริปต์โดยใช้การวางไข่คุณสามารถปรับโครงสร้างสคริปต์ไพ ธ อนเพื่อให้อาร์กิวเมนต์ตัวใดตัวหนึ่งตัดสินใจเลือกฟังก์ชันที่จะเรียกใช้และอาร์กิวเมนต์อื่นจะถูกส่งผ่านไปยังฟังก์ชันนั้น ฯลฯ

หวังว่านี่ชัดเจน แจ้งให้เราทราบหากมีบางสิ่งต้องการความกระจ่าง


17
@ PauloS.Abreu: ปัญหาที่ฉันมีexecคือมันส่งคืนบัฟเฟอร์แทนสตรีมและหากข้อมูลของคุณเกินการmaxBufferตั้งค่าซึ่งมีค่าเริ่มต้นที่ 200kB คุณจะได้รับบัฟเฟอร์ที่เกินข้อยกเว้นและกระบวนการของคุณถูกฆ่า ตั้งแต่การใช้งานลำธารมันมีความยืดหยุ่นมากกว่าspawn exec
NeverForgetY2K

2
เพียงโน้ตเล็ก ๆ ถ้าคุณใช้โหนดคุณไม่ควรใช้คำสำคัญกระบวนการ
alexvicegrab

2
ฉันจะติดตั้งการพึ่งพา pip ภายนอกได้อย่างไร ฉันต้องการ numpy สำหรับโครงการและไม่สามารถเรียกใช้เพราะไม่ได้ติดตั้งไว้
javiergarval

2
@javiergarval นั่นจะเหมาะกว่าสำหรับคำถามใหม่แทนที่จะแสดงความคิดเห็น
NeverForgetY2K

3
มีวิธีอื่นในการส่งคืนข้อมูลจากไพ ธ อนนอกเหนือจากการพิมพ์หรือไม่? ไพ ธ อนสคริปต์ของฉันแสดงผลข้อมูลบันทึกจำนวนมากและเห็นได้ชัดว่ามีปัญหาในการล้างข้อมูลทั้งหมดนั้น
lxknvlk

111

ตัวอย่างสำหรับผู้ที่มาจากพื้นหลัง Python และต้องการรวมโมเดลการเรียนรู้ของเครื่องในแอปพลิเคชัน Node.js:

จะใช้child_processโมดูลหลัก:

const express = require('express')
const app = express()

app.get('/', (req, res) => {

    const { spawn } = require('child_process');
    const pyProg = spawn('python', ['./../pypy.py']);

    pyProg.stdout.on('data', function(data) {

        console.log(data.toString());
        res.write(data);
        res.end('end');
    });
})

app.listen(4000, () => console.log('Application listening on port 4000!'))

ไม่ต้องการsysโมดูลในสคริปต์ Python ของคุณ

ด้านล่างเป็นวิธีการแยกส่วนของการปฏิบัติงานโดยใช้Promise:

const express = require('express')
const app = express()

let runPy = new Promise(function(success, nosuccess) {

    const { spawn } = require('child_process');
    const pyprog = spawn('python', ['./../pypy.py']);

    pyprog.stdout.on('data', function(data) {

        success(data);
    });

    pyprog.stderr.on('data', (data) => {

        nosuccess(data);
    });
});

app.get('/', (req, res) => {

    res.write('welcome\n');

    runPy.then(function(fromRunpy) {
        console.log(fromRunpy.toString());
        res.end(fromRunpy);
    });
})

app.listen(4000, () => console.log('Application listening on port 4000!'))

8
ฉันประหลาดใจที่ไม่ได้รับคะแนนมากขึ้น ในขณะที่คำตอบของ @ NeverForgetY2K นั้นใช้ได้คำตอบนี้มีตัวอย่างที่มีรายละเอียดมากขึ้นรวมถึงการฟังพอร์ตและใช้การประชุม JS ที่ทันสมัยยิ่งขึ้นเช่น const & contract
Mike Williamson

2
ตัวอย่างที่ดี สัญญาหนึ่งดีในการตรวจสอบข้อผิดพลาดบางอย่างที่ฉันมีในสคริปต์ไพ ธ อน
htafoya

38

python-shellโมดูลโดยextrabaconเป็นวิธีที่ง่ายต่อการเรียกใช้สคริปต์หลามจาก Node.js กับพื้นฐาน แต่การสื่อสารระหว่างกระบวนการที่มีประสิทธิภาพและการจัดการข้อผิดพลาดที่ดีขึ้น

การติดตั้ง: npm install python-shell .

ใช้งานสคริปต์ Python อย่างง่าย:

var PythonShell = require('python-shell');

PythonShell.run('my_script.py', function (err) {
  if (err) throw err;
  console.log('finished');
});

การเรียกใช้สคริปต์ Python พร้อมด้วยข้อโต้แย้งและตัวเลือก:

var PythonShell = require('python-shell');

var options = {
  mode: 'text',
  pythonPath: 'path/to/python',
  pythonOptions: ['-u'],
  scriptPath: 'path/to/my/scripts',
  args: ['value1', 'value2', 'value3']
};

PythonShell.run('my_script.py', options, function (err, results) {
  if (err) 
    throw err;
  // Results is an array consisting of messages collected during execution
  console.log('results: %j', results);
});

สำหรับเอกสารฉบับเต็มและรหัสต้นฉบับตรวจสอบhttps://github.com/extrabacon/python-shell


3
ปัญหานี้ทำให้ฉันไม่สามารถใช้งานได้ - github.com/extrabacon/python-shell/issues/179
mhlavacka

1
หากคุณได้รับข้อผิดพลาดนี้ - TypeError: PythonShell.run ไม่ใช่ฟังก์ชั่นตรวจสอบให้แน่ใจว่าคุณนำเข้ามันเช่น var {PythonShell} = นี้ต้องการ ('python-shell');
โมฮัมเหม็

4

ตอนนี้คุณสามารถใช้ไลบรารี RPC ที่สนับสนุน Python และ Javascript เช่นzerorpc

จากหน้าแรกของพวกเขา:

ลูกค้า Node.js

var zerorpc = require("zerorpc");

var client = new zerorpc.Client();
client.connect("tcp://127.0.0.1:4242");

client.invoke("hello", "RPC", function(error, res, more) {
    console.log(res);
});

เซิร์ฟเวอร์หลาม

import zerorpc

class HelloRPC(object):
    def hello(self, name):
        return "Hello, %s" % name

s = zerorpc.Server(HelloRPC())
s.bind("tcp://0.0.0.0:4242")
s.run()

คุณยังสามารถใช้socket.io ได้ทั้ง Node และ Python
Bruno Gabuzomeu

3

คำตอบก่อนหน้าส่วนใหญ่เรียกว่าความสำเร็จของสัญญาในการเปิด ("ข้อมูล") ไม่ใช่วิธีที่เหมาะสมที่จะทำเพราะถ้าคุณได้รับข้อมูลจำนวนมากคุณจะได้รับส่วนแรกเท่านั้น แต่คุณต้องทำในตอนท้าย

const { spawn } = require('child_process');
const pythonDir = (__dirname + "/../pythonCode/"); // Path of python script folder
const python = pythonDir + "pythonEnv/bin/python"; // Path of the Python interpreter

/** remove warning that you don't care about */
function cleanWarning(error) {
    return error.replace(/Detector is not able to detect the language reliably.\n/g,"");
}

function callPython(scriptName, args) {
    return new Promise(function(success, reject) {
        const script = pythonDir + scriptName;
        const pyArgs = [script, JSON.stringify(args) ]
        const pyprog = spawn(python, pyArgs );
        let result = "";
        let resultError = "";
        pyprog.stdout.on('data', function(data) {
            result += data.toString();
        });

        pyprog.stderr.on('data', (data) => {
            resultError += cleanWarning(data.toString());
        });

        pyprog.stdout.on("end", function(){
            if(resultError == "") {
                success(JSON.parse(result));
            }else{
                console.error(`Python error, you can reproduce the error with: \n${python} ${script} ${pyArgs.join(" ")}`);
                const error = new Error(resultError);
                console.error(error);
                reject(resultError);
            }
        })
   });
}
module.exports.callPython = callPython;

โทร:

const pythonCaller = require("../core/pythonCaller");
const result = await pythonCaller.callPython("preprocessorSentiment.py", {"thekeyYouwant": value});

หลาม:

try:
    argu = json.loads(sys.argv[1])
except:
    raise Exception("error while loading argument")

2

ฉันบนโหนด 10 1.0.2และเด็กกระบวนการ ข้อมูลจากไพ ธ อนเป็นอาร์เรย์แบบไบท์และต้องถูกแปลง อีกตัวอย่างที่รวดเร็วของการทำคำขอ http ในหลาม

ปม

const process = spawn("python", ["services/request.py", "https://www.google.com"])

return new Promise((resolve, reject) =>{
    process.stdout.on("data", data =>{
        resolve(data.toString()); // <------------ by default converts to utf-8
    })
    process.stderr.on("data", reject)
})

request.py

import urllib.request
import sys

def karl_morrison_is_a_pedant():   
    response = urllib.request.urlopen(sys.argv[1])
    html = response.read()
    print(html)
    sys.stdout.flush()

karl_morrison_is_a_pedant()

ps ไม่ใช่ตัวอย่างที่ประดิษฐ์เนื่องจากโมดูล http ของโหนดไม่ได้โหลดคำขอบางอย่างที่ฉันต้องทำ


ฉันมีแบ็คเอนด์เซิร์ฟเวอร์ที่ build บน nodejs และฉันมีเครื่องจักรน้อยมากที่เรียนรู้เกี่ยวกับสคริปต์ python ซึ่งฉันวางไข่โดยใช้กระบวนการลูกวางไข่ผ่าน nodejs เมื่อใดก็ตามที่ฉันได้รับการร้องขอบนเซิร์ฟเวอร์ nodejs ของฉัน ตามที่แนะนำในกระทู้นี้ คำถามของฉันคือนี่คือวิธีที่ถูกต้องในการทำมันหรือฉันสามารถให้สคริปต์ python ของฉันทำงานเหมือนบริการขวดที่เชื่อมกับพอร์ตโดยใช้ zmq และเรียกใช้คำสัญญาจาก nodejs ไปยังบริการนี้ สิ่งที่ฉันหมายถึงคืออะไรวิธีการประหยัดหน่วยความจำและวิธีการประหยัดความเร็ว?
Aswin

1
คุณอาจต้องการให้สิ่งที่หลามทำงานอย่างอิสระ คุณไม่ต้องการการพึ่งพารหัสยากโดยเฉพาะอย่างยิ่งสำหรับสิ่งที่ซับซ้อนกว่าในฐานะบริการ ml ถ้าคุณต้องการเพิ่มชิ้นส่วนอื่นในสถาปัตยกรรมนี้ เช่นเดียวกับเลเยอร์แคชที่ด้านหน้าของ ml หรือวิธีเพิ่มพารามิเตอร์พิเศษให้กับโมเดล ml หรือไม่ เป็นหน่วยความจำสำหรับใช้งานเซิร์ฟเวอร์ไพ ธ อน แต่คุณอาจต้องการความยืดหยุ่น หลังจากนั้นคุณสามารถแยกสองเซิร์ฟเวอร์ออกเป็นสองเซิร์ฟเวอร์ได้
1mike12

เขาถามว่าเขาจะเรียกฟังก์ชั่นนี้ไม่ตอบคำถามหรือไม่
K - ความเป็นพิษใน SO กำลังเพิ่มขึ้น

2
@ 1mike12 "karl_morrison_is_a_pedant ()" ฮ่าฮ่าชอบมันมาก!
K - ความเป็นพิษใน SO กำลังเพิ่มขึ้น

0

คุณสามารถใช้ไพ ธ อนของคุณtranspileแล้วเรียกมันราวกับว่ามันเป็นจาวาสคริปต์ ฉันได้กระทำนี้ประสบความสำเร็จสำหรับ screeps และแม้กระทั่งได้ไปทำงานในเบราว์เซอร์แบบ a la brython


0
/*eslint-env es6*/
/*global require*/
/*global console*/
var express = require('express'); 
var app = express();

// Creates a server which runs on port 3000 and  
// can be accessed through localhost:3000
app.listen(3000, function() { 
    console.log('server running on port 3000'); 
} ) 

app.get('/name', function(req, res) {

    console.log('Running');

    // Use child_process.spawn method from  
    // child_process module and assign it 
    // to variable spawn 
    var spawn = require("child_process").spawn;   
    // Parameters passed in spawn - 
    // 1. type_of_script 
    // 2. list containing Path of the script 
    //    and arguments for the script  

    // E.g : http://localhost:3000/name?firstname=Levente
    var process = spawn('python',['apiTest.py', 
                        req.query.firstname]);

    // Takes stdout data from script which executed 
    // with arguments and send this data to res object
    var output = '';
    process.stdout.on('data', function(data) {

        console.log("Sending Info")
        res.end(data.toString('utf8'));
    });

    console.log(output);
}); 

สิ่งนี้ใช้ได้สำหรับฉัน python.exe ของคุณต้องถูกเพิ่มลงในตัวแปรพา ธ ของคุณสำหรับข้อมูลโค้ดนี้ และตรวจสอบให้แน่ใจว่าสคริปต์ python ของคุณอยู่ในโฟลเดอร์โครงการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.