Bei der Suche nach Python-Dekorateuren gab jemand die Aussage ab, dass sie genauso mächtig sind wie Lisp-Makros (insbesondere Clojure).
Wenn ich mir die Beispiele in PEP 318 anschaue, sehe ich so aus, als wären sie nur eine ausgefallene Möglichkeit, einfache alte Funktionen höherer Ordnung in Lisp zu verwenden:
def attrs(**kwds):
def decorate(f):
for k in kwds:
setattr(f, k, kwds[k])
return f
return decorate
@attrs(versionadded="2.2",
author="Guido van Rossum")
def mymethod(f):
...
Ich habe in keinem der Beispiele eine Code-Transformation gesehen, wie in Anatomy of a Clojure Macro beschrieben . Außerdem könnte die fehlende Homoikonizität von Python Code-Transformationen unmöglich machen.
Also, wie vergleichen sich diese beiden und können Sie sagen, dass sie in dem, was Sie tun können, ungefähr gleich sind? Beweise scheinen dagegen zu sprechen.
Bearbeiten: Basierend auf einem Kommentar suche ich nach zwei Dingen: Vergleich auf "so mächtig wie" und "so einfach, um tolle Dinge zu tun".
Antworten:
Ein Dekorateur ist im Grunde nur eine Funktion .
Beispiel in Common Lisp:
Oben ist die Funktion ein Symbol (das von zurückgegeben würde
DEFUN
) und wir fügen die Attribute in die Eigenschaftsliste des Symbols ein .Jetzt können wir es um eine Funktionsdefinition schreiben:
Wenn wir eine ausgefallene Syntax wie in Python hinzufügen möchten, schreiben wir ein Reader-Makro . Mit einem Reader-Makro können wir auf der Ebene der S-Ausdruckssyntax programmieren:
Wir können dann schreiben:
Der Lisp- Leser liest oben:
Jetzt haben wir eine Form von Dekorateuren in Common Lisp.
Makros und Reader-Makros kombinieren.
Eigentlich würde ich oben die Übersetzung in echtem Code mit einem Makro machen, nicht mit einer Funktion.
Die Verwendung erfolgt wie oben mit demselben Reader-Makro. Der Vorteil ist , dass der Lisp - Compiler sieht es immer noch als sogenannte Top-Level - Form - die * Datei Compiler behandelt Top-Level - Formulare speziell zum Beispiel fügt sie Informationen über sie in die Kompilierung- Umgebung . Im obigen Beispiel können wir sehen, dass das Makro in den Quellcode schaut und den Namen extrahiert.
Der Lisp- Leser liest das obige Beispiel in:
Welches wird dann Makro erweitert in:
Makros unterscheiden sich stark von Reader-Makros .
Makros bekommen den Quellcode übergeben, können machen was sie wollen und dann den Quellcode zurückgeben. Die Eingabequelle muss kein gültiger Lisp-Code sein. Es kann alles sein und es könnte ganz anders geschrieben sein. Das Ergebnis muss dann ein gültiger Lisp-Code sein. Wenn der generierte Code jedoch auch ein Makro verwendet, könnte die Syntax des in den Makroaufruf eingebetteten Codes wiederum eine andere sein. Ein einfaches Beispiel: Man könnte ein mathematisches Makro schreiben, das eine Art mathematische Syntax akzeptiert:
Der Ausdruck
y = 3 x ^ 2 - 4 x + 3
ist kein gültiger Lisp-Code, aber das Makro könnte ihn zum Beispiel analysieren und einen gültigen Lisp-Code wie diesen zurückgeben:Es gibt viele andere Anwendungsfälle von Makros in Lisp.
quelle
In Python (der Sprache) können die Dekorateure die Funktion nicht ändern, sondern nur umbrechen, sodass sie definitiv weniger leistungsfähig sind als Lisp-Makros.
In CPython (dem Interpreter) können die Dekoratoren die Funktion ändern, weil sie Zugriff auf den Bytecode haben, aber die Funktion wird zuerst kompiliert und kann dann vom Dekorator manipuliert werden, sodass es nicht möglich ist, die Syntax zu ändern -äquivalent müsste man tun.
Beachten Sie, dass moderne LISPS keine S-Ausdrücke als Bytecode verwenden. Daher funktionieren Makros, die mit S-Ausdruckslisten arbeiten, definitiv vor der Bytecode-Kompilierung, wie oben erwähnt. In Python wird der Decorator danach ausgeführt.
quelle
Es ist ziemlich schwierig, Python-Dekoratoren zu verwenden, um neue Kontrollflussmechanismen einzuführen.
Die Verwendung von Common-Lisp-Makros zur Einführung neuer Kontrollflussmechanismen ist nahezu trivial.
Daraus folgt wahrscheinlich, dass sie nicht gleichermaßen ausdrucksstark sind (ich interpretiere "kraftvoll" als "ausdrucksstark", da ich denke, dass das, was sie tatsächlich bedeuten).
quelle
s/quite hard/impossible/
Es ist sicherlich eine verwandte Funktionalität, aber von einem Python-Dekorator aus ist es nicht trivial, die aufgerufene Methode zu ändern (das wäre der
f
Parameter in Ihrem Beispiel). Um es zu modifizieren, könnten Sie mit dem ast- Modul ( verrückt werden), aber Sie würden sich auf eine ziemlich komplizierte Programmierung einlassen.Dinge in dieser Richtung wurden jedoch getan: Schauen Sie sich das Macropy- Paket an, um einige wirklich umwerfende Beispiele zu finden.
quelle
ast
-transformierende Zeug in Python entspricht nicht den Lisp-Makros. Bei Python sollte die Ausgangssprache Python sein, bei Lisp-Makros kann die von einem Makro transformierte Ausgangssprache buchstäblich alles sein. Daher eignet sich die Python-Metaprogrammierung nur für einfache Dinge (wie AoP), während die Lisp-Metaprogrammierung für die Implementierung leistungsfähiger eDSL-Compiler hilfreich ist.