Python bellen vanuit Java

1. Overzicht

Python is een steeds populairdere programmeertaal, vooral in de wetenschappelijke gemeenschap vanwege de grote verscheidenheid aan numerieke en statistische pakketten. Daarom is het geen ongebruikelijke vereiste om Python-code vanuit onze Java-applicaties aan te roepen.

In deze tutorial we zullen enkele van de meest gebruikelijke manieren bekijken om Python-code vanuit Java aan te roepen.

2. Een eenvoudig Python-script

In deze tutorial gebruiken we een heel eenvoudig Python-script dat we definiëren in een speciaal bestand met de naam hallo.py:

print ("Hallo Baeldung-lezers !!")

Ervan uitgaande dat we een werkende Python-installatie hebben, zouden we het bericht moeten zien wanneer we ons script uitvoeren:

$ python hello.py Hallo Baeldung-lezers !!

3. Core Java

In deze sectie zullen we twee verschillende opties bekijken die we kunnen gebruiken om ons Python-script aan te roepen met behulp van core Java.

3.1. Gebruik makend van ProcessBuilder

Laten we eerst eens kijken hoe we het ProcessBuilder API om een ​​native besturingssysteemproces te maken om te starten Python en voer ons eenvoudige script uit:

@Test openbare ongeldig gegevenPythonScript_whenPythonProcessInvoked_thenSuccess () genereert uitzondering {ProcessBuilder processBuilder = nieuwe ProcessBuilder ("python", resolPythonScriptPath ("hello.py")); processBuilder.redirectErrorStream (true); Proces process = processBuilder.start (); Lijstresultaten = readProcessOutput (process.getInputStream ()); assertThat ("Resultaten mogen niet leeg zijn", results, is (not (empty ()))); assertThat ("Resultaten moeten output van script bevatten:", results, hasItem (containsString ("Hallo Baeldung Readers !!"))); int exitCode = process.waitFor (); assertEquals ("Er mogen geen fouten worden gedetecteerd", 0, exitCode); }

In dit eerste voorbeeld gebruiken we het Python commando met één argument dat het absolute pad naar ons is hallo.py script. We kunnen het vinden in onze test / bronnen map.

Samenvattend creëren we onze ProcessBuilder object doorgeven van de opdracht- en argumentwaarden aan de constructor. Het is ook belangrijk om de oproep naar te vermelden redirectErrorStream (true). Bij eventuele fouten wordt de foutuitvoer samengevoegd met de standaarduitvoer.

Dit is handig omdat het betekent dat we eventuele foutmeldingen van de overeenkomstige uitvoer kunnen lezen wanneer we het getInputStream () methode op de Werkwijze voorwerp. Als we deze eigenschap niet instellen op waar, dan moeten we de uitvoer van twee afzonderlijke streams lezen met behulp van de getInputStream () en de getErrorStream () methoden.

Nu starten we het proces met behulp van de begin() methode om een Werkwijze voorwerp. Vervolgens lezen we de procesoutput en controleren of de inhoud is wat we verwachten.

Zoals eerder vermeld, hebben we aangenomen dat de Python commando is beschikbaar via de PAD variabele.

3.2. Werken met de JSR-223 Scripting Engine

JSR-223, dat voor het eerst werd geïntroduceerd in Java 6, definieert een set scripting-API's die basisscriptfunctionaliteit bieden. Deze methoden bieden mechanismen voor het uitvoeren van scripts en voor het delen van waarden tussen Java en een scripttaal. Het belangrijkste doel van deze standaard was om enige uniformiteit te brengen in de samenwerking met verschillende scripttalen van Java.

We kunnen de pluggable script-engine-architectuur gebruiken voor elke dynamische taal, mits deze natuurlijk een JVM-implementatie heeft. Jython is de Java-platformimplementatie van Python die op de JVM draait.

Ervan uitgaande dat we Jython op de KLASPAD, zou het framework automatisch moeten ontdekken dat we de mogelijkheid hebben om deze script-engine te gebruiken en ons in staat stellen om rechtstreeks naar de Python-script-engine te vragen.

Omdat Jython beschikbaar is via Maven Central, kunnen we het gewoon opnemen in ons pom.xml:

 org.python jython 2.7.2 

Evenzo kan het ook rechtstreeks worden gedownload en geïnstalleerd.

Laten we een lijst maken van alle script-engines die we tot onze beschikking hebben:

ScriptEngineManagerUtils.listEngines ();

Als we de mogelijkheid hebben om Jython te gebruiken, zouden we de juiste script-engine moeten zien:

... Engine naam: jython Versie: 2.7.2 Taal: python Korte namen: python jython 

Nu we weten dat we de Jython-scriptengine kunnen gebruiken, gaan we kijken hoe we onze hallo.py script:

@ Test openbare ongeldig gegevenPythonScriptEngineIsAvailable_whenScriptInvoked_thenOutputDisplayed () gooit Uitzondering {StringWriter-schrijver = nieuwe StringWriter (); ScriptContext context = nieuwe SimpleScriptContext (); context.setWriter (schrijver); ScriptEngineManager manager = nieuwe ScriptEngineManager (); ScriptEngine-engine = manager.getEngineByName ("python"); engine.eval (nieuwe FileReader (resolPythonScriptPath ("hello.py")), context); assertEquals ("Moet scriptuitvoer bevatten:", "Hallo Baeldung-lezers !!", writer.toString (). trim ()); }

Zoals we kunnen zien, is het vrij eenvoudig om met deze API te werken. Eerst beginnen we met het opzetten van een ScriptContext die een StringWriter. Dit wordt gebruikt om de uitvoer op te slaan van het script dat we willen aanroepen.

We gebruiken dan de getEngineByName methode van de ScriptEngineManager klasse om op te zoeken en een ScriptEngine voor een bepaalde korte naam. In ons geval kunnen we slagen Python of jython dat zijn de twee korte namen die aan deze engine zijn gekoppeld.

Net als voorheen is de laatste stap om de uitvoer van ons script te krijgen en te controleren of deze overeenkomt met wat we hadden verwacht.

4. Jython

Als we doorgaan met Jython, hebben we ook de mogelijkheid om Python-code rechtstreeks in onze Java-code in te sluiten. We kunnen dit doen met behulp van de PythonInterpretor klasse:

@ Test openbare ongeldig gegeven PythonInterpreter_whenPrintExecuted_thenOutputDisplayed () {probeer (PythonInterpreter pyInterp = nieuwe PythonInterpreter ()) {StringWriter output = nieuwe StringWriter (); pyInterp.setOut (uitvoer); pyInterp.exec ("print ('Hallo Baeldung-lezers !!')"); assertEquals ("Moet scriptuitvoer bevatten:", "Hallo Baeldung-lezers !!", output.toString () .trim ()); }}

De ... gebruiken PythonInterpreter class stelt ons in staat om een ​​reeks Python-broncode uit te voeren via de exec methode. Zoals eerder gebruiken we een StringWriter om de uitvoer van deze uitvoering vast te leggen.

Laten we nu een voorbeeld bekijken waarin we twee getallen bij elkaar optellen:

@Test openbare leegte gegevenPythonInterpreter_whenNumbersAdded_thenOutputDisplayed () {probeer (PythonInterpreter pyInterp = nieuwe PythonInterpreter ()) {pyInterp.exec ("x = 10 + 10"); PyObject x = pyInterp.get ("x"); assertEquals ("x:", 20, x.asInt ()); }}

In dit voorbeeld zien we hoe we de krijgen methode, om toegang te krijgen tot de waarde van een variabele.

In ons laatste Jython-voorbeeld zullen we zien wat er gebeurt als er een fout optreedt:

probeer (PythonInterpreter pyInterp = nieuwe PythonInterpreter ()) {pyInterp.exec ("import syds"); }

Wanneer we deze code uitvoeren a PyException wordt gegenereerd en we zullen dezelfde fout zien alsof we met native Python zouden werken:

Traceback (meest recente oproep laatste): Bestand "", regel 1, in ImportError: Geen module met de naam syds

Een paar punten die we moeten opmerken:

  • Net zo PythonIntepreter werktuigen AutoCloseable, is het een goede gewoonte om probeer-met-middelen bij het werken met deze klas
  • De PythonInterpreter class name impliceert niet dat onze Python-code wordt geïnterpreteerd. Python-programma's in Jython worden uitgevoerd door de JVM en daarom vóór uitvoering gecompileerd naar Java-bytecode
  • Hoewel Jython de Python-implementatie voor Java is, bevat het mogelijk niet allemaal dezelfde subpakketten als native Python

5. Apache Commons Exec

Een andere bibliotheek van derden die we zouden kunnen gebruiken, is Apache Common Exec, die een aantal tekortkomingen van de Java Process API probeert te verhelpen.

De commons-exec artefact is verkrijgbaar bij Maven Central:

 org.apache.commons commons-exec 1.3 

Laten we nu eens kijken hoe we deze bibliotheek kunnen gebruiken:

@Test openbare ongeldig gegevenPythonScript_whenPythonProcessExecuted_thenSuccess () gooit ExecuteException, IOException {String line = "python" + resolPythonScriptPath ("hello.py"); CommandLine cmdLine = CommandLine.parse (regel); ByteArrayOutputStream outputStream = nieuwe ByteArrayOutputStream (); PumpStreamHandler streamHandler = nieuwe PumpStreamHandler (outputStream); DefaultExecutor executor = nieuwe DefaultExecutor (); executor.setStreamHandler (streamHandler); int exitCode = executor.execute (cmdLine); assertEquals ("Er mogen geen fouten worden gedetecteerd", 0, exitCode); assertEquals ("Moet scriptuitvoer bevatten:", "Hallo Baeldung-lezers !!", outputStream.toString () .trim ()); }

Dit voorbeeld verschilt niet al te veel van ons eerste voorbeeld met ProcessBuilder. We creëren een Opdrachtregel object voor ons gegeven commando. Vervolgens stellen we een stream-handler in om te gebruiken voor het vastleggen van de uitvoer van ons proces voordat we onze opdracht uitvoeren.

Samenvattend: de belangrijkste filosofie achter deze bibliotheek is om een ​​pakket voor procesuitvoering aan te bieden dat gericht is op het ondersteunen van een breed scala aan besturingssystemen via een consistente API.

6. HTTP gebruiken voor interoperabiliteit

Laten we even een stap terug doen en in plaats van Python rechtstreeks aan te roepen, overwegen om een ​​goed ingeburgerd protocol zoals HTTP te gebruiken als een abstractielaag tussen de twee verschillende talen.

In feite wordt Python geleverd met een eenvoudige ingebouwde HTTP-server die we kunnen gebruiken voor het delen van inhoud of bestanden via HTTP:

python -m http.server 9000

Als we nu naar // localhost: 9000, zien we de inhoud vermeld voor de map waar we de vorige opdracht hebben gestart.

Enkele andere populaire frameworks die we zouden kunnen gebruiken voor het maken van robuustere op Python gebaseerde webservices of applicaties zijn Flask en Django.

Zodra we een eindpunt hebben waartoe we toegang hebben, kunnen we een van de verschillende Java HTTP-bibliotheken gebruiken om onze Python-webservice / applicatie-implementatie aan te roepen.

7. Conclusie

In deze zelfstudie hebben we enkele van de meest populaire technologieën geleerd voor het aanroepen van Python-code vanuit Java.

Zoals altijd is de volledige broncode van het artikel beschikbaar op GitHub.