37.4 Alternative Interpreter 

Der im Lieferumfang von Python enthaltene Interpreter CPython kann als Referenzimplementierung der Sprache Python angesehen werden. Er wird von den Kernentwicklern gepflegt, und neue Sprachversionen werden zuerst von CPython unterstützt. Aufgrund der vielseitigen Einsatzzwecke von Python sind im Laufe der Zeit Alternativen zu CPython entstanden, die einen anderen Fokus setzen. In diesem Abschnitt werden die alternativen Interpreter Jython und IronPython besprochen, die in Java bzw. C# implementiert sind und damit eine Verbindung zwischen der Programmiersprache Python und der Java Runtime Environment (JRE) bzw. dem .NET Framework herstellen.
Neben den hier besprochenen Interpretern, die eine Verbindung zu anderen Laufzeitumgebungen herstellen, gibt es Interpreter mit einem anderen Fokus, beispielsweise einer höheren Laufzeit- bzw. Speichereffizienz durch Just-in-Time-Kompilierung. Solche Interpreter, dazu gehört beispielsweise PyPy, werden in Abschnitt 35.6.9 behandelt.
[»] Hinweis
Beachten Sie beim Einsatz alternativer Interpreter, dass diese nicht zwangsläufig die gleiche Sprachversion unterstützen müssen wie die Referenzimplementierung CPython. Es ist im Gegenteil sogar üblich, dass sich neue Sprachversionen, besonders wenn sie große Veränderungen mit sich bringen, erst nach einiger Zeit in den verschiedenen alternativen Interpretern wiederfinden.
Im Folgenden wird zunächst die Interoperabilität zwischen Python und der JRE anhand des Interpreters Jython behandelt. Danach besprechen wir den Interpreter IronPython, der eine Brücke zwischen Python und .NET schlägt.
37.4.1 Interoperabilität mit der Java Runtime Environment – Jython 

Mit der Entwicklung des alternativen Interpreters Jython wurde bereits im Jahr 2000, damals unter dem Namen JPython, begonnen. Jython steht, wie CPython, unter der freien PSF-Lizenz.
[»] Hinweis
Zum Zeitpunkt der Drucklegung dieses Buchs ist Jython für die Python-Sprachversion 2.7 verfügbar. Eine Version, die Python 3 unterstützt, ist in Arbeit, aber noch nicht fertiggestellt.
Zwischen den Sprachversionen 2.x und 3.x gibt es wichtige Unterschiede, über die Sie sich im Migrationskapitel 43, »Von Python 2 nach Python 3«, informieren können.
Installation
Jython können Sie sich unter http://www.jython.org als Java-Applikation herunterladen. Jython setzt zumindest eine installierte Java Runtime Environment (JRE) voraus. Wenn Sie selbst geschriebene Java-Klassen in einem Python-Programm verwenden wollen oder umgekehrt Python-Skripte in ein Java-Programm einbinden möchten, benötigen Sie das Java Development Kit (JDK), das den Java-Compiler beinhaltet.
Jython-Programme ausführen
Die grundlegende Verwendung von Jython unterscheidet sich nicht von der des Referenz-Interpreters CPython. Analog zu CPython lässt sich über den Befehl jython ein Programm ausführen bzw. eine interaktive Jython-Shell öffnen:
>>> $ jython
Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11)
[OpenJDK 64-Bit Server VM (Oracle Corporation)] on java1.8.0_121
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.cos(math.pi)
-1.0
Da Jython eine vollständige Kompatibilität zu Python anstrebt, kann ein Python-Programm, sofern es keine Features von nicht unterstützten Sprachversionen verwendet, jederzeit auch mit Jython ausgeführt werden. Dies trifft zumindest auf die Sprachelemente selbst und große Teile der Standardbibliothek zu.
Unterstützung von Python-Bibliotheken
Module und Pakete für Python, sowohl in der Standardbibliothek als auch von Drittanbietern, können in zwei Arten unterteilt werden: solche, die in Python selbst geschrieben sind, und solche, die als C-Extensions implementiert sind.
Module und Pakete, die in Python implementiert sind, können problemlos auch mit Jython verwendet werden. Dies trifft leider nicht für Module und Pakete der Standardbibliothek zu, die in C implementiert sind. Eine Unterstützung der CPython C API in Jython ist zwar möglich und für zukünftige Versionen geplant, existiert zurzeit aber nicht.
Aus diesem Grund sind in C geschriebene Python-Bibliotheken in Jython nicht verwendbar. Das betrifft Teile der Standardbibliothek, beispielsweise das Paket tkinter, aber auch Drittanbieterbibliotheken wie NumPy oder SciPy. Teile der in C geschriebenen Standardbibliothek von CPython wurden für Jython nach Java bzw. Python portiert, sodass diese problemlos verwendet werden können.
Java-Bibliotheken in Jython-Programmen verwenden
Ein häufiger Anwendungsfall von Jython ist das Einbinden von Java-Bibliotheken – entweder von Teilen der umfangreichen Java-Standardbibliothek oder von Drittanbieterbibliotheken – in Python-Programme. Dies funktioniert analog zum Einbinden von Python-Modulen über das Schlüsselwort import:
>>> from java.util import HashMap
>>> m = HashMap()
>>> m.put(0, "Null")
>>> m.put(1, "Eins")
>>> m
{0: Null, 1: Eins}
In diesem Fall haben wir mit der HashMap das Java-Analogon zu einem Dictionary instanziiert, mit Werten gefüllt und ausgegeben. Die Java-Standardbibliothek ist in Jython über das Paket java verfügbar.
In dieser Art und Weise lassen sich Java-Bibliotheken aus Jython heraus verwenden. Einige Teile der Java-Bibliotheken sind von den Jython-Entwicklern dahingehend angepasst worden, dass sie sich besser in die Sprache Python integrieren. Das betrifft zum Beispiel die oben verwendete HashMap. So lässt sich eine HashMap auch in Kombination mit Sprachelementen von Python verwenden:
>>> m[2] = "Zwei"
>>> for key in m:
... print key, "->", m[key]
...
0 -> Null
1 -> Eins
2 -> Zwei
Für die Verwendung von Java-Objekten in Jython-Code lassen sich die folgenden Regeln aufstellen:
- In einem logischen Ausdruck werden der Wert False des Typs java.lang.Boolean sowie leere Instanzen der Klassen bzw. Interfaces java.util.Vector, java.util. Hashtable, java.util.Dictionary, java.util.List, java.util.Map und java.util. Collection zu False ausgewertet. Alle anderen Instanzen, insbesondere eine ganze Zahl mit dem Wert 0 und ein leerer String, werden zu True ausgewertet.
- Eine for-Schleife kann Instanzen der Klassen java.util.Vector, java.util.Enumeration, java.util.List und java.util.Iterator durchlaufen.
- Ein Index-basierter Zugriff ist auf Instanzen der Klassen java.util.Vector, java. util.Dictionary, java.util.List und java.util.Map möglich. Slicing ist jedoch für keine dieser Klassen implementiert.
- Alle von java.lang.Exception abgeleiteten Klassen können wie Python-Exceptions geworfen und gefangen werden.
-
Jython erlaubt auch eine flexible Verwendung von Python-Datentypen im Java-Kontext. Instanzen werden bei Bedarf in den entsprechenden Java-Datentyp konvertiert:
>>> x = [1,2,3,4]
>>> java.util.Collections.shuffle(x)
>>> x
[2, 1, 4, 3]
>>> type(x)
<type 'list'> - In diesem Fall wurde der Java-Funktion shuffle zum zufälligen Umordnen einer Liste eine Python-Liste übergeben. Das Ergebnis wird wieder als Python-Liste zurückgegeben.
Eigene Java-Objekte einbinden
Neben der Standardbibliothek lassen sich mithilfe von Jython auch selbst geschriebene Java-Objekte einbinden. Dazu definieren wir eine Klasse in einer Datei namens Person.java:
public class Person
{
private String vorname;
private String nachname;
public Person(String vorname, String nachname)
{
this.vorname = vorname;
this.nachname = nachname;
}
public String getName()
{
return vorname + " " + nachname;
}
}
Nachdem die Datei mithilfe des Java-Compilers[ 167 ](in der Regel über den Befehl javac Person.java) zu einer Person.class kompiliert wurde, kann die Klasse Person mithilfe der import-Anweisung eingebunden werden. Danach wird sie verwendet, als wäre es eine Python-Klasse:
>>> import Person
>>> p = Person("Donald", "Duck")
>>> p.getName()
u'Donald Duck'
Anhand des Beispiels erkennen Sie eine weitere Besonderheit von Jython: Java-Strings werden in Unicode-Strings übersetzt.
Python-Programme in Java einbinden
Wie eingangs beschrieben, erlaubt Jython es, aus einem Python-Programm heraus auf die umfangreichen Java-Bibliotheken zuzugreifen oder selbst geschriebene Java-Klassen einzubinden. Die umgekehrte Richtung ist auch möglich: Ein in Java geschriebenes Programm kann mithilfe von Jython auf die Python-Standardbibliothek zugreifen oder Python-Code ausführen.
Im Folgenden möchten wir das Beispiel aus dem vorangegangenen Abschnitt anpassen, sodass die Klasse Person in Python implementiert und aus einem Java-Programm heraus verwendet wird. Dazu muss zunächst ein Java-Interface definiert werden, das die Schnittstelle der Klasse Person beschreibt, zum Beispiel in einer Datei PersonInterface.java:
public interface PersonInterface
{
public String getName();
}
In der Datei python_person.py implementieren wir jetzt das Interface PersonInterface in Form der Klasse Person:
import PersonInterface
class Person(PersonInterface):
def __init__(self, vorname, nachname):
self.vorname = vorname
self.nachname = nachname
def getName(self):
return self.vorname + " " + self.nachname
Die Implementierung unterscheidet sich strukturell nicht von der Java-Implementierung aus dem vorangegangenen Abschnitt. Schließlich schreiben wir ein Java-Hauptprogramm, das den Jython-Interpreter instanziiert, um das Modul person einzubinden:
import org.python.util.PythonInterpreter;
import org.python.core.PyObject;
import org.python.core.PyString;
public class Main
{
public static void main(String[] args)
{
PythonInterpreter interpreter = new PythonInterpreter();
interpreter.exec("from python_person import Person");
PyObject Person = interpreter.get("Person");
PyObject p = Person.__call__(new PyString("Donald"),
new PyString("Duck"));
PersonInterface donald = (PersonInterface)p.__tojava__(
PersonInterface.class);
System.out.println(donald.getName());
}
}
Nach dem Erzeugen des Jython-Interpreters wird dessen Methode exec aufgerufen, um die Klasse Person aus dem Modul person einzubinden. Diese Klasse kann daraufhin im Java-Programm als PyObject-Instanz verwendet werden.
Nachdem die Python-Klasse Person im Java-Programm bekannt ist, kann sie durch explizites Aufrufen der Methode __call__ instanziiert werden. Dabei bekommt sie die gewünschten Namen in Form von Python-Strings übergeben. Das Ergebnis, die Person-Instanz, liegt wieder in Form einer PyObject-Instanz p vor.
In einem letzten Schritt wird p mithilfe der Methode __tojava__ in eine PersonInterface-Instanz konvertiert. Diese kann jetzt verwendet werden, als handle es sich um eine Instanz einer Java-Klasse.
[»] Hinweis
Um das Beispielprogramm kompilieren und ausführen zu können, muss der Java-Umgebung das Paket org.python bekannt gemacht werden. Dieses befindet sich in der Datei jython.jar im Installationsverzeichnis der Jython-Distribution. Zum Kompilieren und Ausführen des Beispielprogramms sind unter Linux die folgenden Befehle nötig:
$ javac -cp /pfad/zu/jython.jar PersonInterface.java Main.java
$ java -cp .:/pfad/zu/jython.jar Main
Unter Windows muss ein Semikolon als Trennzeichen verwendet werden:
$ java -cp .;C:\pfad\zu\jython.jar Main
37.4.2 Interoperabilität mit .NET – IronPython 

Der 2006 zuerst erschienene alternative Interpreter IronPython ist ein vollständig in C# geschriebener Python-Interpreter für die Sprachversion 2.7. Die Implementierung in C# ermöglicht die Anbindung von Python-Programmen an eine Common Language Infrastructure (CLI), beispielsweise Microsofts .NET Framework oder das freie Mono, das unter anderem unter Linux läuft.
IronPython können Sie sich unter http://www.ironpython.net herunterladen.
[»] Hinweis
Zum Zeitpunkt der Drucklegung dieses Buchs ist IronPython für die Python-Sprachversion 2.7 verfügbar. Eine Version, die Python 3 unterstützt, ist in Arbeit, aber noch nicht fertiggestellt.
Zwischen den Sprachversionen 2.x und 3.x gibt es wichtige Unterschiede, über die Sie sich im Migrationskapitel 43, »Von Python 2 nach Python 3«, informieren können.
Die grundlegende Verwendung von IronPython unterscheidet sich nicht von der des Referenz-Interpreters CPython. Analog zu CPython können Sie über die Befehle ipy.exe bzw. ipy64.exe ein Programm ausführen bzw. eine interaktive IronPython-Shell öffnen.[ 168 ](Gegebenenfalls müssen Sie das IronPython-Installationsverzeichnis in den Systempfad eintragen bzw. einen vollen Programmpfad verwenden, beispielsweise C:\Programme\ IronPython\ipy.exe. )
CLI-Bibliotheken in IronPython-Programmen verwenden
In einem IronPython-Programm oder in der interaktiven IronPython-Shell verwenden Sie die import-Anweisung, um CLI-Bibliotheken einzubinden. Diese sind danach nutzbar, als wären sie Python-Module:
>>> import System
>>> System.Console.WriteLine("Hallo Welt")
Hallo Welt
Analog zu Python-Modulen hat ein lokales Modul gleichen Namens beim Einbinden Vorrang. Sollte also ein Modul System.py lokal existieren, wird dieses anstelle des CLI-Namensraums System eingebunden.
Automatische Übersetzung von Datentypen
Ein wichtiger Aspekt bei der Verwendung von Bibliotheken aus fremden Sprachumgebungen ist die Übersetzung der Datentypen. Bei den rudimentären Datentypen kann zwischen Python und der CLI eine 1:1-Beziehung hergestellt werden, die von IronPython so reibungslos wie möglich umgesetzt wird.
Die elementaren Datentypen int, float, bool und str werden von IronPython auf die CLI-Datentypen System.Int32, System.Double, System.Boolean und System.String bzw. System.Char[ 169 ](Der CLI-Datentyp System.Char für ein einzelnes Zeichen hat in Python kein passendes Gegenstück. In Schnittstellen, die eine System.Char-Instanz erwarten, kann ein einstelliger Python-String übergeben werden. ) abgebildet. Durch Einbinden des Moduls clr[ 170 ](für »Common Language Runtime« ) werden Instanzen dieser Python-Datentypen um ihre CLI-Schnittstelle erweitert:
>>> "Python".ToUpper()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'ToUpper'
>>> import clr
>>> "Python".ToUpper()
'PYTHON'
Die Python-Schnittstelle der Basisdatentypen bleibt auch nach dem clr-Import erhalten. Es ist also weiterhin möglich, die bekannte Methode upper zu verwenden.
Die komplexeren Datentypen list, tuple, dict, set und frozenset werden in der CLI-Terminologie generische Typen genannt, da sie als Containertypen Instanzen verschiedener Datentypen enthalten können. Im Gegensatz zu Python darf sich dieser enthaltene Datentyp jedoch nicht ändern, er muss bei der Instanziierung beispielsweise einer Liste angegeben werden. Generische Typen sind im CLI-Namensraum System.Collections.Generic enthalten und können in IronPython folgendermaßen instanziiert werden:
>>> from System.Collections.Generic import List, Dictionary
>>> l = List[int]([1,2,3])
>>> l
List[int]([1, 2, 3])
>>> d = Dictionary[str, int]({"1" : 1})
>>> d
Dictionary[str, int]({'1' : 1})
>>> dict(d)
{'1': 1}
Die in System.Collections.Generic enthaltenen Datentypen werden zunächst mit den zu enthaltenen Datentypen spezialisiert. Dies geschieht in IronPython in eckigen Klammern. Dem Konstruktor kann dann eine Instanz der zugehörigen Python-Datentypen, also beispielsweise list oder dict, übergeben werden. Umgekehrt kann ein generischer Typ in einen dazu passenden Python-Typ konvertiert werden.
[»] Hinweis
Instanzen generischer Typen können erzeugt werden, indem die zur Instanziierung erforderlichen Datentypen in eckige Klammern geschrieben werden. Auf analoge Art und Weise lassen sich generische Methoden aufrufen:
Objekt.GenerischeMethode[typ1, typ2](parameter)
Überladene Methoden aufrufen
Ein weiterer wichtiger Unterschied zu Python ist, dass die CLI das Überladen von Methoden erlaubt. Das bedeutet, dass mehrere gleichnamige Methoden existieren dürfen, die sich anhand ihrer Schnittstelle unterscheiden. Anhand von Anzahl und Typen der übergebenen Parameter wird bei einem Methodenaufruf entschieden, welche der Überladungen konkret aufgerufen wird.
Dieses Konzept gibt es in Python nicht, IronPython versucht aber, anhand der übergebenen Typen die passendste Überladung auszuwählen. Sollten mehrere Überladungen passen, wird ein TypeError geworfen. In einem solchen Fall kann die gewünschte Überladung ausgewählt werden. Dazu besitzt jede Methode das Overloads-Objekt, über das eine Überladung anhand ihrer Schnittstelle ausgewählt werden kann:
>>> from System import String
>>> String.Compare.Overloads[str, str]("Hallo", "Welt")
-1
In diesem Fall wurde die Variante der String-Methode Compare aufgerufen, die zwei Strings als Parameter erwartet.
Assemblys in IronPython-Programmen einbinden
IronPython kann CLI-Assemblys laden. Das sind kompilierte Bibliotheken, die in einer beliebigen CLI-Sprache, beispielsweise C#, geschrieben werden können und unter Windows üblicherweise im DLL-Format vorliegen.
Zum Laden eines Assemblys wird die Funktion AddReference aus dem Modul clr verwendet:
>>> import clr
>>> clr.AddReference("System.Web")
>>> import System.Web
Beim Start von IronPython werden automatisch die Assemblys mscorelib.dll und System.dll geladen. Weitere benötigte Assemblys müssen über AddReference nachgeladen werden.
Python-Komponenten in CLI-Programmen einbinden
Mithilfe von IronPython lassen sich CLI-Assemblys aus einem Python-Programm heraus nutzen. Der umgekehrte Weg, ein Python-Skript in ein CLI-Programm, beispielsweise ein C#-Programm, einzubinden, ist ebenfalls möglich. Dazu enthält IronPython die Assemblys IronPython und Microsoft.Scripting, mit deren Hilfe sich beispielsweise Funktionen aus einem externen Python-Skript aufrufen lassen:[ 171 ](Um das Beispiel kompilieren zu können, müssen dem Compiler die Assemblys IronPython.dll und Microsoft.Scripting.dll aus dem IronPython-Installationsverzeichnis bekannt gemacht werden: csc.exe /reference:IronPython.dll /reference:Microsoft.Scripting.dll main.cs)
using IronPython.Hosting;
class Beispiel
{
static void Main(string[] args)
{
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
engine.ExecuteFile("script.py", scope);
System.Console.Write(
engine.Operations.Invoke(scope.GetVariable("quadrat"),5)
);
}
}
Zunächst wird die Python-Umgebung engine instanziiert und ein Scope scope erzeugt. Unter einem Scope versteht man einen Teilbereich eines Programms, außerhalb dessen lokale Variablen ihre Sichtbarkeit verlieren. Mithilfe der Engine lässt sich nun ein Python-Skript ausführen. Dazu wird die Methode ExecuteFile aufgerufen und der Scope übergeben, in dem das Skript ausgeführt werden soll.
Nachdem das Skript ausgeführt wurde, können Variablen aus dem Scope ausgelesen werden. Dazu zählen auch Funktionen wie die Beispielfunktion quadrat, die über die Methode engine.Operations.Invoke ausgeführt werden. Das zum Beispiel passende Python-Skript sieht folgendermaßen aus:
def quadrat(x):
return x**2
In analoger Art und Weise lässt sich über die Methode engine.Execute Python-Code ausführen, der nicht in einer externen Datei, sondern als String im C#-Programm selbst vorliegt.