Python et Javascript s'appellent mutuellement super detailed explanation (latest January 2022) (i) Basic Principles Part 1 - through subprocess and Inter - process communication (IPC)

Milliele 2022-01-15 03:08:13 阅读数:997

python et javascript appellent mutuellement

La première chose à comprendre est,javascriptEtpythonC'est un langage interprétatif,Leur fonctionnement exige desruntimeDe.

  • Python: Nous installons le plus souventPythonEn fait, oui.cpython,Est basé surCPour courir.En plus de ça, il y a des choses commepypyC'est comme ça que j'ai écrit l'interpréteur moi - même,transcryptC'est devenujsÀ utiliser plus tardjsDeruntimeDe.En gros,,Non utilisécpythonEn tant quepythonDeruntimeLe plus gros problème avecpypiCes paquets étrangers installés,Il y en a mêmecpythonSon sac original(CommecollectionsCe genre de)Ça ne marchera pas.
  • JavaScript: Les moteurs de course courants sontgoogleDeV8,MozillaDeSpiderMonkeyAttendez un peu!,Ces moteurs vontJavaScriptConversion de code en code machine exécution.Moteur de course basé sur ces bases,Nous pouvons développer le soutienJSNavigateur pour(Par exemple,ChromeDeJSFaire tourner le moteur, c'estV8);D'autres fonctionnalités peuvent également être développéesJSEnvironnement opérationnel,Par exemple,Node.js,Ça veut dire qu'on n'a pas besoin d'un navigateur,Ou courirJSCode.C'est bon.Node.js,JSLa gestion des paquets est également devenue beaucoup plus pratique,Si nous voulons développerNode.jsPack pour le navigateur,C'est la raison pour laquelleNode.jsCode source compilé dans le support du navigateurJSCode.

Dans cet article,Hypothèses:

  • Langue principale : Langue de la procédure principale finale
  • Langue secondaire: Une autre langue qui n'est pas la langue principale

Par exemple,pythonAppelezjs,python C'est la langue principale ,js C'est un langage mineur

TL; DR

Pour:

  1. pythonEtjavascriptDeruntime(En gros, c'estcpython[Non, pas du tout.cython!]EtNode.js)Tout est prêt.
  2. Le langage auxiliaire utilise des paquets complexes(Par exemplepythonÇa marche.numpy、javascriptAvec un peu deNode.jsDeC++Extension, etc.)
  3. Si nécessaire pour l'efficacité opérationnelle:
    • pythonAvecjavascriptIl n'y a pas beaucoup d'interaction entre,L'objet transmis n'est pas trop grand、C'est trop compliqué.,De préférence tous les objets sérialisables
    • javascriptLa proportion n'est pas si faible.Sinon,pythonTunejsEt si,DémarrageNode.jsLe Sous - processus est plus lent que le programme réel;jsTunepythonEt si,Parce quejsVite!,Il faut beaucoup de temps pour attendrepythonAllez..
  4. Parce queIPCLe taux approximatif synchronisera l'entrée et la sortie avec le thread,Moins de langue principale, plus de processus、Programmation simultanée comme thread

Il y a une bibliothèque.!Il y a une bibliothèque.!Il y a une bibliothèque.!

pythonTunejavascript

  • JSPyBridgepip install javascript
    • Avantages:
      1. L'auteur maintient toujours,RetourissueEt les mises à jour sont assez rapides.
      2. Prise en charge despythonEtnodeVersion,Facile à installer
      3. Support de base pour les appels mutuels,Y compris les fonctions de liaison ou de rappel.
    • Inconvénients:Absence de mécanisme de destruction raisonnable,import javascriptEst considéré comme une connexionJSFin,Tous les Threads multithreads à utiliser sont initialisés.SipythonLe programme principal veut redémarrer la paireJSConnexion à,Ou le programme principal utilise plusieurs processus,Je veux me connecter une fois par processusJS,C'est difficile à faire.,Il est facile de faire des erreurs.
  • PyExecJSpip install PyExecJS,Les anciens articles techniques poussent ce paquet
    • Avantages: Soutien saufNode.jsAutresruntime,Par exemplePhantomJSQuelque chose comme ça.
    • Inconvénients: End of Life,L'auteur a arrêté la maintenance

javascriptTunepython

(Parce que ça ne correspond pas vraiment à mes besoins de projet,Donc je ne sais pas grand - chose)

Principes

Tout d'abord,,La méthode repose sur la prémisse que les deux langues ont été installéesruntime,Et peut être appelé par la ligne de commanderuntimeExécuter un fichier ou un script de chaîne de caractères.Par exemple,Chargez.cpythonAprès ça, on pourra passer parpython a.pyPour courirpythonProcédure,Chargez.Node.jsAprès ça, on pourra passer parnode a.jsOunode -e "some script"Attends de courirJSProcédure.

Bien sûr.,Dans le cas le plus simple,Si nous n'avons besoin d'appeler qu'une seule langue secondaire,Et il n'y a pas d'interaction(Ou une seule interaction au plus),Alors trouvez un appel de méthode.CLIJusteOKC'est.Pour l'entrée dans la langue secondairestdinOu passer un paramètre en ligne de commande,Lire la sortie de la commande comme sortie du sous - langage.
Par exemple,pythonÇa marchesubprocess.Popen,subprocess.call,subprocess.check_outputOuos.systemQuelque chose comme ça.,Node.jsÇa marchechild_processLa méthode de Li,execOuforkQuelque chose comme ça..Il est important de noter que,Si d'autres paquets doivent être référencés,Node.jsIl convient de noter quenode_modulesExécuter les instructions dans le répertoire où elles se trouvent,pythonAttention au réglagePYTHONPATHVariables d'environnement.

# Need to set the working directory to the directory where `node_modules` resides if necessary
>>> import subprocess
>>> a, b = 1, 2
>>> print(subprocess.check_output(["node", "-e", f"console.log({a}+{b})"]))
b'3\n'
>>> print(subprocess.check_output(["node", "-e", f"console.log({a}+{b})"]).decode('utf-8'))
3
// Need to set PYTHONPATH in advance if necessary
const a = 1;
const b = 2;
const { execSync } = require("child_process");
console.log(execSync(`python -c "print(${a}+${b})"`));
//<Buffer 33 0a>
console.log(execSync(`python -c "print(${a}+${b})"`).toString());
//3
//

S'il y a des interactions complexes,Pour passer des objets complexes,Certains peuvent être sérialisés,Certains ne peuvent pas être sérialisés du tout,Et alors??
Il s'agit essentiellement d'utiliserCommunication inter - processus(IPC),Habituellement avecTuyauterie(Pipe).Instdin,stdoutEtstderrChoisissez au moins un des trois pour construire le pipeline.
Supposons que j'utilisestdinDepythonVersjsTransmission des données,AvecstderrRéception des données,C'est à peu près comme ça.:
(Le pseudocode suivant n'est qu'indicatif,Pas rigoureusement testé,Utilisation pratique recommandée utilisation directe de la Bibliothèque)

  1. Créer une nouvelle sous - langue(Supposons queJS)Documentationpython-bridge.js:Ce fichier est constamment LustdinEt selon le message envoyé,Traitement différent;Et si vous avez besoin d'imprimer un message ou de le transmettreobjectPour la langue principale,Les sérialiser correctement et les écrirestdoutOustderr.
    process.stdin.on('data', data => {
    data.split('\n').forEach(line => {
    // Deal with each line
    // write message
    process.stdout.write(message + "\n");
    // deliver object, "$j2p" can be any prefix predefined and agreed upon with the Python side
    // just to tell python side that this is an object needs parsing
    process.stderr.write("$j2p sendObj "+JSON.stringify(obj)+"\n);
    });
    }
    process.on('exit', () => {
    console.debug('** Node exiting');
    });
    
  2. InpythonMoyenne,AvecPopenOuvrir un sous - processus asynchrone,Et au moins un des sous - processus,Par canalisation.C'est comme:
    cmd = ["node", "--trace-uncaught", f"{os.path.dirname(__file__)}/python-bridge.js"]
    kwargs = dict(
    stdin=subprocess.PIPE,
    stdout=sys.stdout,
    stderr=subprocess.PIPE,
    )
    if os.name == 'nt':
    kwargs['creationflags'] = subprocess.CREATE_NO_WINDOW
    subproc = subprocess.Popen(cmd, **kwargs)
    
  3. Appelé au besoinJS,Ou besoin de donnerJSLors de la transmission des données,Allez.subprocÉcrire des informations sérialisées,Après l'écritureflush,Sinon, le tampon pourrait être écrit en premier:
    subproc.stdin.write(f"$p2j call funcName {json.dumps([arg1, arg2])}".encode())
    subproc.stdin.flush() # write immediately, not writing to the buffer of the stream
    
  4. Pour la tuyauteriestdout/stderr,Créer un nouveau thread,Dédié à la lecture et au traitement des données transmises.Est la conversion d'un objet en objet,Est l'impression directe d'informations générales au processus principalstderrOustdout.
    def read_stderr():
    while subproc.poll() is None:
    # when the subprocess is still alive, keep reading
    line = self.subproc.stderr.readline().decode('utf-8')
    if line.startswith('$j2p'):
    # receive special information
    _, cmd, line = line.split(' ', maxsplit=2)
    if cmd == 'sendObj':
    # For example, received an object
    obj = json.loads(line)
    else:
    # otherwise, write to stderr as it is
    sys.stderr.write(line)
    stderr_thread = threading.Thread(target=read_stderr, args=(), daemon=True)
    stderr_thread.start()
    
    C'est à cause de notrestdoutAucun tuyau n'a été construit,Alors...nodePar làstdoutCe qui est imprimé à l'intérieur sera imprimé directement àpythonDesys.stdout- Oui.,Ne vous en occupez pas vous - même..
  5. Parce que les fils sont asynchrones,Quand Savez - vous qu'un objet retourné par une fonction est arrivé?La réponse est de synchroniser les fils,Sémaphore(Semaphore)、Conditions(Condition),Événements(Event)Attendez un peu!,Tout va bien..ParpythonConditionsPar exemple:
    func_name_cv = threading.Condition()
    # use a flag and a result object in case some function has no result
    func_name_result_returned = False
    func_name_result = None
    def func_name_wrapper(arg1, arg2):
    # send arguments
    subproc.stdin.write(f"$p2j call funcName {json.dumps([arg1, arg2])}".encode())
    subproc.stdin.flush()
    # wait for the result
    with func_name_cv:
    if not func_name_result_returned:
    func_name_cv.wait(timeout=10000)
    # when result finally returned, reset the flag
    func_name_result_returned = False
    return func_name_result
    
    En même temps,Besoin de lirestderrLes fils deread_stderrPour débloquer cette valeur de retour.Il est important de noter que,SiJSDuan est sorti par accident.,subprocEt mourir.,N'oubliez pas d'annuler le blocage dans le fil principal.
    def read_stderr():
    while subproc.poll() is None:
    # when the subprocess is still alive, keep reading
    # Deal with a line
    line = self.subproc.stderr.readline().decode('utf-8')
    if line.startswith('$j2p'):
    # receive special information
    _, cmd, line = line.split(' ', maxsplit=2)
    if cmd == 'sendObj':
    # acquire lock here to ensure the editing of func_name_result is mutex
    with func_name_cv:
    # For example, received an object
    func_name_result = json.loads(line)
    func_name_result_returned = True
    # unblock func_name_wrapper when receiving the result
    func_name_cv.notify()
    else:
    # otherwise, write to stderr as it is
    sys.stderr.write(line)
    # If subproc is terminated (mainly due to error), still need to unblock func_name_wrapper
    func_name_cv.notify()
    
    Bien sûr, c'est une version plus simple,Parce queJSLes appels sont essentiellement linéaires,On peut donc savoir qu'il suffit d'en avoir unobjectRetour de,Ça doit êtrefunc_name_wrapperRésultats correspondants.Si les fonctions sont nombreuses,La situation sera plus compliquée.
  6. Si tu veuxAnnulez.JSConnexion à,Vous devez d'abord fermer le Sous - processus,Et attendez de lirestdout/stderrLes fils de,EnfinN'oubliez pas de fermer le tuyau.EtL'ordre de ces trois étapes ne peut pas être changé,Si vous fermez d'abord le tuyau,Lire le thread parce questdout/stderrC'est fermé et une erreur s'est produite.
    subproc.terminate()
    stderr_thread.join()
    subproc.stdin.close()
    subproc.stderr.close()
    

Si c'est le cas,javascriptAppelezpython,C'est la même méthode,javascriptFang Oui.Node.jsEt si,Avecchild_processLes instructions de.

Avantages

  1. Tout ce qu'il faut, c'est un montage normal des deux côtésruntimePour réaliser l'interaction,L'environnement de fonctionnement est relativement bon.
  2. Tant quepythonFang etjavascriptFang dans leursruntimeÇa va aller.,Il n'y a pas de problème à courir même après.(Sauf en cas de concurrence)
  3. Tous les paquets d'extension disponibles dans les deux langues sont essentiellement pris en charge.

Inconvénients

  1. QuandpythonAvecJavaScriptInteractions fréquentes,Et quand l'information interactive est grande,Peut affecter l'efficacité du programme.Parce que ce n'est que par3Les tuyaux sont mélangés pour traiter les informations générales à imprimer、pythonAvecjsObjets interactifs、Appels de fonctions, etc.,Les communications coûtent cher.
  2. Pour qu'un autre sous - processus exécute le Sous - langageruntime,Ça va prendre du temps et de l'espace.
版权声明:本文为[Milliele]所创,转载请带上原文链接,感谢。 https://javamana.com/2022/01/202201150250134373.html