Pony ORM macht den netten Trick, einen Generatorausdruck in SQL zu konvertieren. Beispiel:
>>> select(p for p in Person if p.name.startswith('Paul'))
.order_by(Person.name)[:2]
SELECT "p"."id", "p"."name", "p"."age"
FROM "Person" "p"
WHERE "p"."name" LIKE "Paul%"
ORDER BY "p"."name"
LIMIT 2
[Person[3], Person[1]]
>>>
Ich weiß, dass in Python eine wunderbare Selbstbeobachtung und Metaprogrammierung integriert ist, aber wie kann diese Bibliothek den Generatorausdruck ohne Vorverarbeitung übersetzen? Es sieht aus wie Magie.
[aktualisieren]
Blender schrieb:
Hier ist die Datei , nach der Sie suchen. Es scheint den Generator mit Hilfe einer Selbstbeobachtungs-Zauberei zu rekonstruieren. Ich bin nicht sicher, ob es 100% der Python-Syntax unterstützt, aber das ist ziemlich cool. - Mixer
Ich dachte, sie würden einige Funktionen des Generator-Ausdrucksprotokolls untersuchen, aber diese Datei durchsuchen und das betreffende ast
Modul sehen ... Nein, sie überprüfen die Programmquelle nicht im laufenden Betrieb, oder? Unglaublich...
@BrenBarn: Wenn ich versuche, den Generator außerhalb des select
Funktionsaufrufs aufzurufen, ist das Ergebnis:
>>> x = (p for p in Person if p.age > 20)
>>> x.next()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "<interactive input>", line 1, in <genexpr>
File "C:\Python27\lib\site-packages\pony\orm\core.py", line 1822, in next
% self.entity.__name__)
File "C:\Python27\lib\site-packages\pony\utils.py", line 92, in throw
raise exc
TypeError: Use select(...) function or Person.select(...) method for iteration
>>>
Es scheint, als würden sie mehr arkane Beschwörungsformeln ausführen, z. B. den select
Funktionsaufruf überprüfen und den Grammatikbaum der abstrakten Python-Syntax im laufenden Betrieb verarbeiten.
Ich würde immer noch gerne jemanden sehen, der es erklärt, die Quelle liegt weit über meinem Zauberer-Level.
p
Objekt ein Objekt eines Typs von Pony implementiert , dass schaut, was Methoden / Eigenschaften auf sie zugegriffen wird ( zum Beispielname
,startswith
) und wandelt sie in SQL.Antworten:
Pony ORM Autor ist hier.
Pony übersetzt den Python-Generator in drei Schritten in eine SQL-Abfrage:
Der komplexeste Teil ist der zweite Schritt, in dem Pony die "Bedeutung" von Python-Ausdrücken verstehen muss. Anscheinend interessiert Sie der erste Schritt am meisten. Lassen Sie mich erklären, wie das Dekompilieren funktioniert.
Betrachten wir diese Abfrage:
Welches wird in die folgende SQL übersetzt:
Und unten ist das Ergebnis dieser Abfrage, die ausgedruckt wird:
Die
select()
Funktion akzeptiert einen Python-Generator als Argument und analysiert dann seinen Bytecode. Wir können Bytecode-Anweisungen dieses Generators mit dem Standard-Python-dis
Modul erhalten:Pony ORM hat die Funktion
decompile()
innerhalb des Modulspony.orm.decompiling
, die einen AST aus dem Bytecode wiederherstellen kann:Hier sehen wir die Textdarstellung der AST-Knoten:
Mal sehen, wie die
decompile()
Funktion funktioniert.Die
decompile()
Funktion erstellt einDecompiler
Objekt, das das Besuchermuster implementiert. Die Dekompilerinstanz erhält nacheinander Bytecode-Anweisungen. Für jede Anweisung ruft das Dekompilerobjekt eine eigene Methode auf. Der Name dieser Methode entspricht dem Namen der aktuellen Bytecode-Anweisung.Wenn Python einen Ausdruck berechnet, wird ein Stapel verwendet, in dem ein Zwischenergebnis der Berechnung gespeichert wird. Das Dekompilerobjekt hat auch einen eigenen Stapel, aber dieser Stapel speichert nicht das Ergebnis der Ausdrucksberechnung, sondern den AST-Knoten für den Ausdruck.
Wenn die Dekompilierungsmethode für den nächsten Bytecode-Befehl aufgerufen wird, werden AST-Knoten vom Stapel genommen, zu einem neuen AST-Knoten kombiniert und dieser Knoten dann oben auf dem Stapel platziert.
Lassen Sie uns zum Beispiel sehen, wie der Unterausdruck
c.country == 'USA'
berechnet wird. Das entsprechende Bytecode-Fragment lautet:Das Dekompilerobjekt führt also Folgendes aus:
decompiler.LOAD_FAST('c')
. Diese Methode platziert denName('c')
Knoten oben auf dem Dekompiler-Stapel.decompiler.LOAD_ATTR('country')
. Diese Methode nimmt denName('c')
Knoten vom Stapel, erstellt denGeattr(Name('c'), 'country')
Knoten und legt ihn oben auf den Stapel.decompiler.LOAD_CONST('USA')
. Diese Methode legt denConst('USA')
Knoten oben auf den Stapel.decompiler.COMPARE_OP('==')
. Diese Methode nimmt zwei Knoten (Getattr und Const) aus dem Stapel und legt sie dannCompare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])
oben auf den Stapel.Nachdem alle Bytecode-Anweisungen verarbeitet wurden, enthält der Dekompiler-Stapel einen einzelnen AST-Knoten, der dem gesamten Generatorausdruck entspricht.
Da Pony ORM nur Generatoren und Lambdas dekompilieren muss, ist dies nicht so komplex, da der Befehlsfluss für einen Generator relativ einfach ist - es handelt sich nur um eine Reihe verschachtelter Schleifen.
Derzeit deckt Pony ORM den gesamten Generatorbefehlssatz ab, mit Ausnahme von zwei Dingen:
a if b else c
a < b < c
Wenn Pony auf einen solchen Ausdruck stößt, wird die
NotImplementedError
Ausnahme ausgelöst . Aber auch in diesem Fall können Sie es zum Laufen bringen, indem Sie den Generatorausdruck als Zeichenfolge übergeben. Wenn Sie einen Generator als String übergeben, verwendet Pony das Dekompilierermodul nicht. Stattdessen wird der AST mit der Standard-Python-compiler.parse
Funktion abgerufen .Hoffe das beantwortet deine Frage.
quelle