Sådan kaldes Python fra Java

1. Oversigt

Python er et stadig mere populært programmeringssprog, især i det videnskabelige samfund på grund af dets rige udvalg af numeriske og statistiske pakker. Derfor er det ikke et ualmindeligt krav at kunne påkalde Python-kode fra vores Java-applikationer.

I denne vejledning vi kigger på nogle af de mest almindelige måder at ringe til Python-kode fra Java.

2. Et simpelt Python-script

I hele denne tutorial bruger vi et meget simpelt Python-script, som vi definerer i en dedikeret fil kaldet hej.py:

print ("Hej Baeldung-læsere !!")

Forudsat at vi har en fungerende Python-installation, når vi kører vores script, skal vi se beskeden udskrevet:

$ python hello.py Hej Baeldung-læsere !!

3. Core Java

I dette afsnit ser vi på to forskellige muligheder, vi kan bruge til at påberåbe vores Python-script ved hjælp af core Java.

3.1. Ved brug af ProcessBuilder

Lad os først se på, hvordan vi kan bruge ProcessBuilder API til at oprette en indbygget operativsystemproces, der skal startes python og udfør vores enkle script:

@Test offentligt ugyldigt givetPythonScript_whenPythonProcessInvoked_thenSuccess () kaster undtagelse {ProcessBuilder processBuilder = ny ProcessBuilder ("python", resolutionPythonScriptPath ("hello.py")); processBuilder.redirectErrorStream (sand); Process proces = processBuilder.start (); Listeresultater = readProcessOutput (process.getInputStream ()); assertThat ("Resultaterne skal ikke være tomme", resultater, er (ikke (tomme ()))); assertThat ("Resultaterne skal indeholde output af script:", results, hasItem (indeholderString ("Hello Baeldung Readers !!")); int exitCode = process.waitFor (); assertEquals ("Ingen fejl skal detekteres", 0, exitCode); }

I dette første eksempel kører vi python kommando med et argument, som er den absolutte vej til vores hej.py manuskript. Vi kan finde det i vores test / ressourcer folder.

For at opsummere opretter vi vores ProcessBuilder objekt, der sender kommando- og argumentværdierne til konstruktøren. Det er også vigtigt at nævne opkaldet til redirectErrorStream (sand). I tilfælde af fejl flettes fejloutputtet med standardoutputtet.

Dette er nyttigt, da det betyder, at vi kan læse eventuelle fejlmeddelelser fra den tilsvarende output, når vi ringer til getInputStream () metode til Behandle objekt. Hvis vi ikke indstiller denne ejendom til rigtigt, så bliver vi nødt til at læse output fra to separate streams ved hjælp af getInputStream () og getErrorStream () metoder.

Nu starter vi processen ved hjælp af Start() metode til at få en Behandle objekt. Derefter læser vi procesoutputtet og kontrollerer, at indholdet er, hvad vi forventer.

Som tidligere nævnt har vi antaget, at python kommandoen er tilgængelig via STI variabel.

3.2. Arbejde med JSR-223 Scripting Engine

JSR-223, som først blev introduceret i Java 6, definerer et sæt scripting-API'er, der giver grundlæggende scriptfunktionalitet. Disse metoder tilvejebringer mekanismer til udførelse af scripts og til deling af værdier mellem Java og et script-sprog. Hovedformålet med denne standard var at forsøge at skabe en vis ensartethed i interoperation med forskellige script-sprog fra Java.

Vi kan bruge den plugbare scriptmotorarkitektur til ethvert dynamisk sprog, forudsat at det selvfølgelig har en JVM-implementering. Jython er Java-platformimplementeringen af ​​Python, der kører på JVM.

Forudsat at vi har Jython på CLASSPATH, skal rammen automatisk opdage, at vi har muligheden for at bruge denne scriptmotor og gøre det muligt for os at bede om Python-scriptmotoren direkte.

Da Jython er tilgængelig fra Maven Central, kan vi bare inkludere det i vores pom.xml:

 org.python jython 2.7.2 

På samme måde kan den også downloades og installeres direkte.

Lad os liste over alle de scriptmotorer, som vi har til rådighed for os:

ScriptEngineManagerUtils.listEngines ();

Hvis vi har mulighed for at bruge Jython, skal vi se den relevante scripting-motor vises:

... Motornavn: jython Version: 2.7.2 Sprog: python Kortnavne: python jython 

Nu hvor vi ved, at vi kan bruge Jython-scriptmotoren, lad os gå videre og se, hvordan vi kalder vores hej.py manuskript:

@Test offentlig ugyldighed givenPythonScriptEngineIsAvailable_whenScriptInvoked_thenOutputDisplayed () kaster undtagelse {StringWriter forfatter = ny StringWriter (); ScriptContext context = ny SimpleScriptContext (); context.setWriter (forfatter); ScriptEngineManager manager = ny ScriptEngineManager (); ScriptEngine engine = manager.getEngineByName ("python"); engine.eval (ny FileReader (resolutionPythonScriptPath ("hello.py")), kontekst); assertEquals ("Skal indeholde scriptoutput:", "Hej Baeldung-læsere !!", writer.toString (). trim ()); }

Som vi kan se, er det ret simpelt at arbejde med denne API. Først begynder vi med at oprette en ScriptContext der indeholder en StringWriter. Dette bruges til at gemme output fra det script, vi vil påberåbe.

Vi bruger derefter getEngineByName metode til ScriptEngineManager klasse for at slå op og oprette en ScriptEngine for et givet kort navn. I vores tilfælde kan vi passere python eller jython hvilke er de to korte navne, der er knyttet til denne motor.

Som før er det sidste trin at få output fra vores script og kontrollere, at det svarer til det, vi forventede.

4. Jython

Fortsat med Jython har vi også muligheden for at indlejre Python-kode direkte i vores Java-kode. Vi kan gøre dette ved hjælp af PythonInterpretor klasse:

@Test offentligt ugyldigt givetPythonInterpreter_whenPrintExecuted_thenOutputDisplayed () {try (PythonInterpreter pyInterp = ny PythonInterpreter ()) {StringWriter output = ny StringWriter (); pyInterp.setOut (output); pyInterp.exec ("print ('Hello Baeldung Readers !!')"); assertEquals ("Skal indeholde scriptoutput:", "Hej Baeldung-læsere !!", output.toString () .trim ()); }}

Bruger PythonTolk klasse giver os mulighed for at udføre en streng af Python kildekode via udføre metode. Som før bruger vi en StringWriter for at fange output fra denne udførelse.

Lad os nu se et eksempel, hvor vi tilføjer to tal sammen:

@Test offentligt ugyldigt givetPythonInterpreter_whenNumbersAdded_thenOutputDisplayed () {prøv (PythonInterpreter pyInterp = ny PythonInterpreter ()) {pyInterp.exec ("x = 10 + 10"); PyObject x = pyInterp.get ("x"); assertEquals ("x:", 20, x.asInt ()); }}

I dette eksempel ser vi, hvordan vi kan bruge metode for at få adgang til værdien af ​​en variabel.

I vores sidste Jython-eksempel ser vi, hvad der sker, når der opstår en fejl:

prøv (PythonInterpreter pyInterp = ny PythonInterpreter ()) {pyInterp.exec ("import syds"); }

Når vi kører denne kode a PyException kastes, og vi får vist den samme fejl, som hvis vi arbejdede med native Python:

Traceback (seneste opkald sidst): Fil "", linje 1, i ImportError: Intet modul med navnet syds

Et par punkter, vi skal bemærke:

  • Som PythonIntepreter redskaber Kan lukkes automatisk, det er god praksis at bruge prøv med ressourcer når du arbejder med denne klasse
  • Det PythonTolk klasse navn betyder ikke, at vores Python-kode fortolkes. Python-programmer i Jython køres af JVM og kompileres derfor til Java bytecode før udførelse
  • Selvom Jython er Python-implementeringen til Java, indeholder den muligvis ikke alle de samme underpakker som native Python

5. Apache Commons Exec

Et andet tredjepartsbibliotek, som vi kunne overveje at bruge, er Apache Common Exec, som forsøger at overvinde nogle af manglerne ved Java Process API.

Det commons-exec artefakt fås fra Maven Central:

 org.apache.commons commons-exec 1.3 

Lad os nu, hvordan vi kan bruge dette bibliotek:

@Test offentligt ugyldigt givetPythonScript_whenPythonProcessExecuted_thenSuccess () kaster ExecuteException, IOException {String line = "python" + resolutionPythonScriptPath ("hello.py"); CommandLine cmdLine = CommandLine.parse (linje); ByteArrayOutputStream outputStream = ny ByteArrayOutputStream (); PumpStreamHandler streamHandler = ny PumpStreamHandler (outputStream); DefaultExecutor eksekutor = ny DefaultExecutor (); executor.setStreamHandler (streamHandler); int exitCode = executor.execute (cmdLine); assertEquals ("Ingen fejl skal detekteres", 0, exitCode); assertEquals ("Skal indeholde scriptoutput:", "Hej Baeldung-læsere !!", outputStream.toString () .trim ()); }

Dette eksempel er ikke alt for anderledes end vores første eksempel ProcessBuilder. Vi opretter en Kommandolinje objekt til vores givne kommando. Dernæst oprettede vi en streamhåndtering, der skal bruges til at optage output fra vores proces, før vi udfører vores kommando.

For at opsummere er hovedfilosofien bag dette bibliotek at tilbyde en procesudførelsespakke, der har til formål at understøtte en bred vifte af operativsystemer gennem en konsistent API.

6. Brug af HTTP til interoperabilitet

Lad os tage et skridt tilbage et øjeblik, og i stedet for at forsøge at påberåbe Python overveje direkte at bruge en veletableret protokol som HTTP som et abstraktionslag mellem de to forskellige sprog.

Faktisk leveres Python med en simpel indbygget HTTP-server, som vi kan bruge til at dele indhold eller filer via HTTP:

python -m http.server 9000

Hvis vi nu går til // localhost: 9000, ser vi indholdet, der er anført for den mappe, hvor vi startede den forrige kommando.

Nogle andre populære rammer, vi kunne overveje at bruge til at skabe mere robuste Python-baserede webtjenester eller applikationer, er Flask og Django.

Når vi først har et slutpunkt, som vi har adgang til, kan vi bruge et hvilket som helst af flere Java HTTP-biblioteker til at påkalde vores Python-webtjeneste / applikationsimplementering.

7. Konklusion

I denne vejledning har vi lært om nogle af de mest populære teknologier til opkald til Python-kode fra Java.

Som altid er artiklens fulde kildekode tilgængelig på GitHub.