Kako nazvati Python s Jave

1. Pregled

Python je sve popularniji programski jezik, posebno u znanstvenoj zajednici zbog bogate raznolikosti numeričkih i statističkih paketa. Stoga nije rijedak zahtjev za pozivanjem Python koda iz naših Java aplikacija.

U ovom vodiču, pogledati ćemo neke od najčešćih načina pozivanja Python koda s Jave.

2. Jednostavna skripta na Pythonu

Kroz ovaj tutorial koristit ćemo vrlo jednostavnu Python skriptu koju ćemo definirati u namjenskoj datoteci pod nazivom zdravo.py:

ispis ("Pozdrav čitateljima Baeldung !!")

Pod pretpostavkom da imamo ispravnu instalaciju Pythona, kada pokrenemo našu skriptu trebali bismo vidjeti ispisanu poruku:

$ python hello.py Pozdrav čitateljima Baeldung !!

3. Jezgra Java

U ovom ćemo odjeljku pogledati dvije različite opcije koje možemo koristiti za pozivanje naše Python skripte pomoću jezgre Java.

3.1. Koristeći ProcessBuilder

Pogledajmo prvo kako možemo koristiti ProcessBuilder API za stvaranje matičnog procesa za pokretanje operativnog sustava piton i izvršite našu jednostavnu skriptu:

@Test javna praznina givenPythonScript_whenPythonProcessInvoked_thenSuccess () baca izuzetak {ProcessBuilder processBuilder = novi ProcessBuilder ("python", resolPythonScriptPath ("hello.py")); processBuilder.redirectErrorStream (true); Procesni postupak = processBuilder.start (); Navesti rezultate = readProcessOutput (process.getInputStream ()); assertThat ("Rezultati ne smiju biti prazni", rezultati jesu (nije (prazno ()))); assertThat ("Rezultati trebaju sadržavati izlaz skripte:", results, hasItem (containsString ("Hello Baeldung Readers !!"))); int exitCode = process.waitFor (); assertEquals ("Ne smiju se otkriti pogreške", 0, izlazni kod); }

U ovom prvom primjeru pokrećemo piton naredba s jednim argumentom koji je apsolutni put do našeg zdravo.py skripta. Možemo ga pronaći u našem test / resursi mapu.

Da rezimiramo, mi stvaramo svoje ProcessBuilder objekt koji predaje vrijednosti naredbe i argumenta konstruktoru. Također je važno spomenuti poziv na redirectErrorStream (true). U slučaju bilo kakvih pogrešaka, izlaz pogreške spojit će se sa standardnim izlazom.

To je korisno jer znači da možemo pročitati sve poruke o pogreškama iz odgovarajućeg izlaza kada pozovemo getInputStream () metoda na Postupak objekt. Ako ovo svojstvo ne postavimo na pravi, tada ćemo trebati pročitati izlaz iz dva odvojena toka, koristeći getInputStream () i getErrorStream () metode.

Sada započinjemo postupak pomoću početak() metoda za dobivanje a Postupak objekt. Zatim čitamo izlazne podatke procesa i provjeravamo je li sadržaj ono što očekujemo.

Kao što je prethodno spomenuto, pretpostavili smo da piton naredba je dostupna putem STAZA varijabilna.

3.2. Rad s JSR-223 skriptnim motorom

JSR-223, koji je prvi put predstavljen u Javi 6, definira skup API-ja za skriptiranje koji pružaju osnovne funkcije skriptiranja. Te metode pružaju mehanizme za izvršavanje skripti i za razmjenu vrijednosti između Jave i skriptnog jezika. Glavni cilj ovog standarda bio je pokušati unijeti određenu ujednačenost u interakciju s različitim skriptnim jezicima s Jave.

Možemo koristiti plug-in arhitekturu skriptnog motora za bilo koji dinamički jezik pod uvjetom da ima JVM implementaciju, naravno. Jython je implementacija Java platforme Pythona koja radi na JVM-u.

Pod pretpostavkom da imamo Jythona na KLASPAT, okvir bi trebao automatski otkriti da imamo mogućnost korištenja ovog mehanizma za skriptiranje i omogućiti nam da izravno tražimo Python skriptni mehanizam.

Budući da je Jython dostupan u Maven Central, možemo ga jednostavno uključiti u naš pom.xml:

 org.python jython 2.7.2 

Isto tako, može se i preuzeti i instalirati izravno.

Nabrojimo sve skriptne mehanizme koje imamo na raspolaganju:

ScriptEngineManagerUtils.listEngines ();

Ako imamo mogućnost korištenja Jythona, trebali bismo vidjeti prikazan odgovarajući mehanizam za skriptiranje:

... Naziv motora: jython Verzija: 2.7.2 Jezik: python Kratka imena: python jython 

Sad kad znamo da možemo koristiti Jython skriptni stroj, idemo naprijed i vidjet ćemo kako nazvati naš zdravo.py skripta:

@Test javna praznina givenPythonScriptEngineIsAvailable_whenScriptInvoked_thenOutputDisplayed () baca iznimku {StringWriter writer = new StringWriter (); ScriptContext context = new SimpleScriptContext (); context.setWriter (pisac); ScriptEngineManager manager = novi ScriptEngineManager (); ScriptEngine engine = manager.getEngineByName ("python"); engine.eval (novi FileReader (resolPythonScriptPath ("hello.py")), kontekst); assertEquals ("Treba sadržavati izlaz skripte:", "Pozdrav čitateljima Baeldung !!", writer.toString (). trim ()); }

Kao što vidimo, prilično je jednostavno raditi s ovim API-jem. Prvo započinjemo postavljanjem a ScriptContext koji sadrži a StringWriter. To će se koristiti za pohranu rezultata iz skripte koju želimo pozvati.

Zatim koristimo getEngineByName metoda ScriptEngineManager razred potražiti i stvoriti a ScriptEngine za dano kratko ime. U našem slučaju možemo proći piton ili jython koja su dva kratka imena povezana s ovim motorom.

Kao i prije, posljednji je korak dobiti izlaz iz naše skripte i provjeriti odgovara li onome što smo očekivali.

4. Jython

Nastavljajući s Jythonom, također imamo mogućnost ugrađivanja Python koda izravno u naš Java kôd. To možemo učiniti pomoću PythonInterpretor razred:

@Test javna praznina givenPythonInterpreter_whenPrintExecuted_thenOutputDisplayed () {try (PythonInterpreter pyInterp = new PythonInterpreter ()) {StringWriter output = new StringWriter (); pyInterp.setOut (izlaz); pyInterp.exec ("print ('Pozdrav čitateljima Baeldung !!')"); assertEquals ("Treba sadržavati izlaz skripte:", "Pozdrav čitateljima Baeldung !!", output.toString () .trim ()); }}

Koristiti PythonInterpreter klasa omogućuje nam izvršavanje niza izvornog koda Pythona putem izvršiti metoda. Kao i prije, koristimo a StringWriter da uhvati izlaz iz ovog izvršavanja.

Pogledajmo sada primjer gdje zbrajamo dva broja:

@Test javna praznina givenPythonInterpreter_whenNumbersAdded_thenOutputDisplayed () {try (PythonInterpreter pyInterp = new PythonInterpreter ()) {pyInterp.exec ("x = 10 + 10"); PyObject x = pyInterp.get ("x"); assertEquals ("x:", 20, x.asInt ()); }}

U ovom primjeru vidimo kako možemo koristiti dobiti metoda, za pristup vrijednosti varijable.

U našem posljednjem primjeru Jythona vidjet ćemo što se događa kada se dogodi pogreška:

pokušajte (PythonInterpreter pyInterp = novi PythonInterpreter ()) {pyInterp.exec ("uvoz syds"); }

Kada pokrenemo ovaj kod a PyException bačen je i vidjet ćemo istu pogrešku kao da radimo s izvornim Pythonom:

Traceback (najnoviji zadnji poziv): Datoteka "", red 1, u ImportError: Nema modula s imenom syds

Nekoliko točaka koje bismo trebali napomenuti:

  • Kao PythonIntepreter provodi AutoCloseable, dobra je praksa za upotrebu pokušajte s resursima prilikom rada s ovom nastavom
  • The PythonInterpreter naziv klase ne znači da se naš Python kôd tumači. Python programe u Jythonu pokreće JVM i stoga se prevode u Java bajt kod
  • Iako je Jython Python implementacija za Javu, on možda neće sadržavati sve iste podpakete kao izvorni Python

5. Apache Commons Exec

Druga biblioteka treće strane koju bismo mogli razmotriti je Apache Common Exec koja pokušava prevladati neke nedostatke Java Process API-ja.

The commons-exec Artefakt je dostupan u Maven Central:

 org.apache.commons commons-exec 1.3 

Ajmo sada kako možemo koristiti ovu knjižnicu:

@Test javna praznina givenPythonScript_whenPythonProcessExecuted_thenSuccess () baca ExecuteException, IOException {String line = "python" + resolPythonScriptPath ("hello.py"); CommandLine cmdLine = CommandLine.parse (linija); ByteArrayOutputStream outputStream = novi ByteArrayOutputStream (); PumpStreamHandler streamHandler = novi PumpStreamHandler (outputStream); Zadani izvršilac izvršitelj = novi zadani izvršitelj (); izvršitelj.setStreamHandler (streamHandler); int exitCode = executor.execute (cmdLine); assertEquals ("Ne smiju se otkriti pogreške", 0, izlazni kod); assertEquals ("Treba sadržavati izlaz skripte:", "Pozdrav čitateljima Baeldung !!", outputStream.toString () .trim ()); }

Ovaj primjer nije previše sličan našem prvom primjeru koji se koristi ProcessBuilder. Mi stvaramo CommandLine objekt za našu zadanu naredbu. Dalje, postavili smo obrađivač toka koji će se koristiti za hvatanje rezultata iz našeg procesa prije izvršavanja naše naredbe.

Da rezimiramo, glavna filozofija koja stoji iza ove knjižnice je ponuditi paket izvršenja procesa čiji je cilj podrška širokom spektru operativnih sustava putem dosljednog API-ja.

6. Korištenje HTTP-a za interoperabilnost

Vratimo se na trenutak natrag i umjesto pokušaja pozivanja Pythona izravno razmislite o korištenju dobro uspostavljenog protokola poput HTTP-a kao sloja apstrakcije između dva različita jezika.

Zapravo se Python isporučuje s jednostavnim ugrađenim HTTP poslužiteljem koji možemo koristiti za dijeljenje sadržaja ili datoteka putem HTTP-a:

python -m http.server 9000

Ako sada odemo do // localhost: 9000, vidjet ćemo sadržaj naveden za direktorij u kojem smo pokrenuli prethodnu naredbu.

Neki drugi popularni okviri koje bismo mogli razmotriti za stvaranje robusnijih web usluga ili aplikacija temeljenih na Pythonu su Flask i Django.

Jednom kada imamo krajnju točku kojoj možemo pristupiti, možemo koristiti bilo koju od nekoliko Java HTTP knjižnica za pozivanje naše implementacije web usluge / aplikacije Python.

7. Zaključak

U ovom uputstvu naučili smo o nekim najpopularnijim tehnologijama za pozivanje Python koda s Jave.

Kao i uvijek, puni izvorni kôd članka dostupan je na GitHub-u.