Python für Android

Du hast eine Idee für ein Projekt?
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Das neue Android 2.2 brachte mich auf den Gedanken: Wäre es nicht interessant, eine App auch mit vertrauter Python-Syntax bauen zu können? Java ist so schwergewichtig.

Und ich meine jetzt nicht ein CPython, dass man per NDK kompiliert hat und dessen Laufzeitsystem wahrscheinlich 10x größer ist, als eine durchschnittliche App (obwohl, die AIR-Runtime ist wohl auch 20 MB groß...) und auch nicht Jython, das man versuchen könnte, irgendwie auf der Dalvik-VM zum Laufen zu bringen (darf keinen Java-Bytecode erzeugen), sondern dachte eher an den Ansatz von Duby. Könnte man nicht ein Subset von Python relativ gradlinig in Java-Quelltext übersetzen, um diesen dann "ganz normal" in eine Android-App zu kompilieren? Dazu eine pythonifizierte Bibliothek für die wichtigsten APIs und der Spaß kann losgehen.

Oder ist das eine dumme Idee?

Ich habe so gar keine Erfahrung mit Android-Apps, aber wie schwer kann das schon sein? ;)

Ich würde mir vorstellen, das "ast"-Modul zu benutzen, um aus Python-Quelltext Java-Code zu generieren, mit einem Laufzeitsystem zu verbinden, daraus ein komplettes Projekt (vielleicht mit Maven oder für Eclipse) zu generieren, was man dann bestimmt mit irgendwelchen Kommandozeilen-Werkzeugen aus dem Android-SDK zu einer App machen und auf dem Emulator ausprobieren kann. Vielleicht ist der Roundtripp zu lang. Vielleicht braucht man eine Art Laufzeit-Emulation direkt in Python. Das müsste man ausprobieren.

Stefan
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Habe mich auch nur flüchtig mit Android und der ASE beschäftigt.

Aber ich denke, dass dein hier geschilderter Weg die einzige Möglichkeit ist, eine akzeptable Performance zu kriegen und gleichzeitig nicht auf Funktionen der Dalvik VM verzichten zu müssen.

Allerdings habe ich keine Ahnung davon, ob sich das so einfach realisieren lässt.
Oder ist das eine dumme Idee?
-1 -> Ausprobieren...;-)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Hier ist ein POC, der allerdings noch keines der schweren Probleme löst. Wie findet man für zu übersetzende Klassen z.B. die richtigen Typen der Exemplarvariablen und Methoden? Oder soll das immer "java.lang.Object" sein?

Code: Alles auswählen

import ast

class State:
    def __init__(self):
        self.indent = 0
    
    def pr(self, s):
        if s == "}":
            self.indent -= 1
        print("  " * self.indent + s)
        if s.endswith("{"):
            self.indent += 1

class Methods:
    def Module_translate(self, state):
        state.pr("package test {")
        state.pr("public class Test {")
        state.pr("public static void main(String[] args) {")
        for expr in self.body:
            expr.translate(state)
        state.pr("}")
        state.pr("}")
        state.pr("}")
    
    def Expr_translate(self, state):
        state.pr(self.value.translate(state) + ";")
    
    def Call_translate(self, state):
        return "%s(%s)" % (self.func.translate(state), ", ".join(arg.translate(state) for arg in self.args))
    
    def Name_translate(self, state):
        if self.id == "print":
            return "System.out.println"
        return self.id
    
    def BinOp_translate(self, state):
        return "%s %s %s" % (self.left.translate(state), self.op.name(), self.right.translate(state))
    
    def Num_translate(self, state):
        return self.n
    
    def Add_name(self):
        return "+"
    
for k, v in Methods.__dict__.items():
    if k[0] == '_': continue
    m, n = k.split('_', 1)
    setattr(getattr(ast, m), n, v)

ast.parse("print(1+2)").translate(State())
Stefan
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Für das PyPy Projekt wird ein Subset von Python namens RPython verwendet. Darin geschriebene Programme werden dann in andere Sprachen (JVM, CLR, C) übersetzt. Ob das nur speziell für Interpreter funktioniert oder für beliebige Programme weiss ich nicht. Wäre aber sicherlich ein interessanter Ansatzpunkt.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Pypy ist bestimmt einen Blick wert. Ohne mich detailliert damit beschäftigt zu haben, scheint mir das Projekt aber recht umfangreich und der Einstieg daher aufwendig zu sein. Dokumentation ist auch nicht so direkt zu finden.

Ich hätte die Hoffnung, einen direkteren Weg zu finden. Python geeignet zu beschränken, ist aber sicherlich der richtige Weg. Ich erwähnte ja schon Duby, welches ein Ruby-lookalike ist, das allerdings eher Java-Semantik hat. Das wäre eher mein Ansatz, als zu versuchen, die Dynamik von Python komplett umsetzen zu wollen.

Ich hatte daher mein Beispiel-Projekt schon mal Dython getauft :)

Ich weiß, Pypy geht so vor, dass sie nicht den Quelltext übersetzen, sondern die geladenen Module, da dies Metaprogrammierung auf Modulebene (insbesondere @-Decorators) erlaubt. Dafür müsste man wahrscheinlich Bytecode und nicht Quelltext, so wie ich das mit dem "ast"-Modul machen wollte, als Grundlage für die Übersetzung nehmen. Ginge aber auch, macht es nur ein bisschen aufwendiger, erst mal wieder einen AST zu bauen.

Oder... siehe Folgeposting.

Ach, und im Gegensatz zu Pypy würde ich gerne Fall Python 3.x als Grundlage haben wollen, da ich dort optional Typen an Funktionen annotieren kann und der String-Datentyp besser zu Java passt.

Stefan
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Gegeben sei

Code: Alles auswählen

def f(a): return a + 1
Daraus könnte ich ein Java-Objekt machen:

Code: Alles auswählen

vm.locals.setItem("f", new UserFunc("f", vm.globals, new Code() {
  { varnames = new String[]{"a"}; consts = new Object[]{new Integer(1)}; }
  void apply(VM vm) {
    vm.push(vm.locals[0]); // a
    vm.push(consts[0]);
    vm.add();
  }
}));
Dazu muss man das "f"-Objekt untersuchen und die relevanten Informationen extrahieren. Da Python-Funktionen komplexer sind und z.B. Schlüsselwort- und Rest-Parameter sowie Default-Werte erlauben, muss man hier noch mehr Aufwand treiben, doch das Prinzip bleibt das selbe: Es muss Java-Code erzeugt werden, der beim Ausführen wieder das passende Laufzeit-Objekt erzeugt.

Damit könnte man sehr nahe an der Python-Semantik bleiben, doch effizient ist das nicht gerade.

Wahrscheinlich reicht ja auch folgendes:

Code: Alles auswählen

public int f(int a) { return a + 1; }
Möglicherweise will man jedoch nicht auf first-class-Funktionen verzichten und wenn der Einsatz von Reflection hier zu langsam ist, muss man doch jede Funktion in eine eigene Klasse stecken. Idealerweise könnte man diese beiden Implementationsansätze einfach umschalten, um so Benchmarks zu machen.

Stefan
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Wie wäre es denn, typisierte Methoden vorauszusetzen (Python-3-Annotations) und das ganze zu schönem Java-Code zu übersetzen? Sollte ja eigentlich nicht sehr schwer sein. (Aufwändig aber allemal :-)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Dieses Beispiel (abgeleitet aus dem Android-Tutorial):

Code: Alles auswählen

from android.app import Activity
from android.os import Bundle
from android.widget import TextView

class Start(Activity):
    def onCreate(state: Bundle):
        super().onCreate(state)
        tv = TextView(self)
        tv.setText("Hallo, Welt")
        self.setContentView(tv)
kann mein naiver Übersetzer so umformen:

Code: Alles auswählen

package sma.test;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class Start extends Activity {
  public void onCreate(Bundle state) {
    super.onCreate(state);
    Object tv = TextView(this);
    tv.setText("Hallo, Welt");
    this.setContentView(tv);
  }
}
Dies zeigt drei Probleme: Ich erkenne keine Klassen, damit keine Konstruktoraufrufe und weiß nicht, dass vor `TextView` ein `new` gehört. Ich kenne nicht den Typ der lokalen Variable `tv` und habe daher den falschen Typ gewählt. Das mit den Imports funktioniert nur zufällig. Typischerweise würde man in Python ja eher ein Modul importieren und dann `os.Bundle` als Name benutzen.

Ich denke, man kann diese Probleme mit einer Typanalyse des Programms lösen. Ich muss wissen (oder einfach annehmen), dass alles, was importiert wird, eine Klasse ist. Dann weiß ich schon mal, wo "new" hinkommt. Ich könnte zwar Parameter explizit wie im Beispiel typisieren, doch bei lokalen Variablen muss das Programm das selbst wissen. Und zwar auch noch in weniger einfachen Fällen wie dem Beispielfall. Ich muss den Typ einer jeden linken Seite einer Zuweisung kennen und dadurch neuen Variablen Typen zuordnen können. Damit das funktioniert, muss ich insbesondere die Rückgabetypen aller Methoden aller Java-Klassen kennen. Dies könnte man vielleicht irgendwie aus der Bibliothek extrahieren und dann als Metadaten benutzen.

Hat jemand Erfahrung mit Typanalyse. Würde ein naiver Ansatz, der rekursiv durch den AST läuft, in jedem Fall funktionieren?

Stefan
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

sma hat geschrieben: Ich hatte daher mein Beispiel-Projekt schon mal Dython getauft :)
Musste da gerade an die Staubsauger denken ;).


Habe ich das richtig verstanden, dass du Java mit Python-Syntax haben willst?

Hört sich interessant an. Hast du ein öffentliches Projekt erstellt? Vielleicht wäre das ja für mich ein passende Gelgenheit mich mit Java auseinander zu setzen.
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Hätte auch Lust :-)

sma, du kannst du bei Callable-Aufrufen einfach gucken, ob der (die, das?) Callee eine Klasse ist, oder?

Edit: In deinem Beispiel ist `Object tv` ungeschickt, du kannst da doch Type Inference anwenden :-)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Dauerbaustelle, um zu erkennen, dass ein Call-Knoten zu einem Konstruktoraufruf werden soll, brauche ich ja genau die die erwähnte Typanalyse, die ermitteln muss, dass der Typ des func-Knotens "Class" ist. Das ist nicht so trivial, denn die beiden folgenden Varianten sollten ja auch gehen:

Code: Alles auswählen

from foo import Bar as Baz; Baz()
foo = Baz; foo()
Das falsche "Object" ist natürlich ebenfalls der fehlenden Typanalyse geschuldet. Momentan habe ich Java-Code, der überhaupt nicht kompiliert. Alternative wäre, einfach überall Object zu benutzen und an jeder Stelle, wo man eine Variable an eine bekannte Methode übergibt, hier den Typ einfach zu casten und andernfalls reflections zu benutzen. Das wäre dann wohl wie der "dynamic"-Typ von C#.

jbs, du hast recht, ich möchte prinzipiell ein Java mit Python-Syntax. Ein bisschen stärker nach Python darf es sich aber anfühlen. So ist die Frage, ob folgendes gehen sollte oder nicht:

Code: Alles auswählen

class C:
    def m(self, x): return x + 1
print(map(C.m, (1, 2)))
Wäre "map" Teil eines Laufzeitsystems? Wie würde man Funktionsobjekte realisieren? Wie realisiert man Tupel? Gibt es ein Laufzeitsystem. Bei Duby hatte sich headius dafür entschieden, dass es explizit kein Laufzeitsystem gibt, sondern Methoden nur (per Makro) auf andere abgebildet werden. Somit müsste "map" zu einer expliziten for-each-Schleife in Java werden. Oder aber man setzt die Google-Collection-Bibliothek als zwingend voraus. Diese kennt eine statische "Lists.transform()"-Funktion und funktioniert ja wohl hoffentlich auch unter Android.

Ich setze morgen oder übermorgen mal ein github-Projekt auf und würde mich freuen, wenn ihr (und auch andere) mitmachen.

Stefan
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

sma, Java hat sowas wie `Method` und `Class`, so könnte man Funktions/Klassenobjekte darstellen. Aber eigentlich sollte der Übersetzer so gut sein, dass man sowas nicht braucht ;)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Unter http://github.com/sma/dython findet ihr meinen Beispielcode. Dieser ist für Python 3.1.

Ich habe auch angefangen, eine Typanalyse zu bauen. Das, so befürchte ich, hat aber locker den Umfang und die Komplexität einer Diplomarbeit. "Mal eben" ein Typsystem für Python entwickeln ist nicht trivial. Ich befürchte, man kommt nicht ohne Typvariablen, Funktionstypen und Vereinigungstypen aus. Und eigentlich will man auch noch Unifikation von Typschemen (oder wie das genau heißt, also aus a+1 ableiten, dass der Typ von "a" int oder float sein müsste).

Und es gibt immer hässliche Ausnahmen: Bei der eingebauten Funktion `open` ist der Typ des Ergebnis abhängig davon, ob im optionalen zweiten Argument ein "b" steht. Bäh. Ein Tupel kann sowohl als Tupeltyp oder als unveränderliche Sequenz benutzt werden. Und auch Listen könnte man wie Tupeltypen benutzen. Nochmal bäh.

Ich bin mir unsicher, wie es am besten weitergehen sollte. Ich glaube, ich bräuchte ein konkretes nicht-triviales Python-Beispiel, an dem man sich lang hangeln kann. Das Ziel, dass für Android mit der Android-Klassenbibliothek zu kompilieren würde ich dazu erst mal zurückstellen und schauen, dass das gültiges Java wird und prinzipiell funktioniert - auch wenn sich I/O auf die Kommandozeile beschränkt.

Stefan
Santi95
User
Beiträge: 10
Registriert: Donnerstag 29. März 2012, 16:14

vieleicht hilft euch hier ja etwas weiter:
http://code.google.com/p/android-script ... loads/list
vorlallem die ersten punkte!
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Der Thread ist zwei Jahre alt...
Santi95
User
Beiträge: 10
Registriert: Donnerstag 29. März 2012, 16:14

trz für leute die mal nach dem thema suchen^^
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ich glaube, schon vor 2 Jahren, als ich den ersten Beitrag schrieb, gab es die erwähnte Scripting-Bridge. Das war aber nicht, was ich suchte. Angelehnt an Duby, welches jetzt Mirah heißt und von dem man auch nix mehr hört, war die Idee, ein Python mit Java-Semantik zu haben, mit dem es bequemer ist, z.B. Android-Anwendungen zu schreiben, als direkt mit Java.

Zwei Jahre später kenne ich Android und behaupte, einfach nur die Syntax austauschen (wenn man die angesprochenen Herausforderungen mit der Typanalyse lösen kann) macht Android-Programmierung auch nicht einfacher. Erfindet man nicht gleichzeitig auch noch eine neue GUI-Bibliothek bleibt die Programmierung umständlich und kleinteilig und ist durchsetzt mit XML.

Einige behaupten, die Zukunft für mobile Anwendungen ist HTML5. Doch gerade Android-basierte Geräte sind so vielfältig, das es schwer ist, eine Lösung für alles zu benutzen und außerdem häufig so günstig, dass sie für HTML5 nicht leistungsstark genug sind. Und wenn's bezahlt wird, mache ich auch gerne Java :)

Stefan
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ja, gleich im zweiten Beitrag. ASE heißt jetzt S4A und das was du verlinkt hast ist offenbar der Python-Interpreter von S4A.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
heiliga horsd

Leonidas hat geschrieben:Ja, gleich im zweiten Beitrag. ASE heißt jetzt S4A und das was du verlinkt hast ist offenbar der Python-Interpreter von S4A.
Ok, war mir nicht bewusst!
Antworten